[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: return macro
From: |
Damien Mattei |
Subject: |
Re: return macro |
Date: |
Mon, 28 Jun 2021 10:53:53 +0200 |
thank Taylan
i already use all that in the past (
https://github.com/damien-mattei/library-FunctProg/blob/master/syntactic-sugar.scm#L133
)
but it was late ,past 1h00 and i forget it, and it was not compatible with
R5RS
thank for your long and clear explainations...
i did not use let/ec because the problem was not with let/ec or call/cc but
with the macro hygiene
and i wanted to stay compatible with other scheme,let/ec should be guile
only and i suppose let/ec made with call/cc .
regards,
damien
On Mon, Jun 28, 2021 at 3:15 AM Taylan Kammer <taylan.kammer@gmail.com>
wrote:
> On 28.06.2021 01:10, Damien Mattei wrote:
> > hi,
> >
> > i wanted to create a macro that is used like a function definition and
> > allow return using call/cc:
> >
> > (define-syntax def
> > (syntax-rules (return)
> > ((_ (name args ...) body body* ...)
> > (define name (lambda (args ...)
> > (call/cc (lambda (return) body body* ...)))))
> > ((_ name expr) (define name expr))))
> >
> > unfortunaly i got error:
> >
> > scheme@(guile-user)> (def (test x) (cond ((= x 3) 7) ((= x 2) (return
> 5))
> > (else 3)))
> > ;;; <stdin>:2:42: warning: possibly unbound variable `return'
> > scheme@(guile-user)> (test 2)
> > ice-9/boot-9.scm:1685:16: In procedure raise-exception:
> > Unbound variable: return
> >
> >
> > any idea?
>
> Hi Damien,
>
> This is because of the "hygiene" rule of Scheme, where the notion of
> "lexical
> scope" is taken very seriously: for an identifier to be bound, it must be
> visible in the lexical (textual) surroundings where it has been bound.
>
> So for instance, in the following example code:
>
> (def (test x)
> (cond
> ((= x 3) (return 7))
> ((= x 2) (return 5))))
>
> We can't see a binding for "return" anywhere in the text, therefore it
> cannot
> be bound.
>
> This is good "default" behavior because it makes code more flexible and
> easier
> to understand.
>
> An easy way of overcoming this issue is to let the user explicitly name the
> return identifier however they like:
>
> (define-syntax def
> (syntax-rules ()
> ((_ (name ret arg ...) body body* ...)
> (define (name arg ...)
> (call/cc (lambda (ret) body body* ...))))))
>
> Now you could define:
>
> (def (test return x)
> (cond
> ((= x 3) (return 7))
> ((= x 2) (return 5))))
>
> Or for instance:
>
> (def (test blubba x)
> (cond
> ((= x 3) (blubba 7))
> ((= x 2) (blubba 5))))
>
> However, sometimes you're sure that you want to make an implicit binding
> for
> an identifier, and for those cases you need to write an "unhygienic" macro
> which can be achieved with the more complex macro system "syntax-case".
>
> Here's how your desired macro could be defined. I will use identifiers
> like
> "<foo>" just for easier readability; they don't have any special meaning:
>
> (define-syntax def
> (lambda (stx)
> (syntax-case stx ()
> ((_ (<name> <arg> ...) <body> <body>* ...)
> (let ((ret-id (datum->syntax stx 'return)))
> #`(define (<name> <arg> ...)
> (call/cc (lambda (#,ret-id) <body> <body>* ...))))))))
>
> There's a few things here to take note of:
>
> - Unlike with syntax-rules, the syntax-case is contained in a lambda which
> takes a single argument: a "syntax object" which is passed to syntax-case
>
> - Unlike with syntax-rules, the "body" of the macro (where it begins with a
> 'let') is not immediately part of the generated code; that 'let' is
> actually
> executed during compile-time. The body of the macro must result in an
> object of the type "syntax object" that represents the generated code.
>
> - You see that I define a variable called "ret-id" which I bind to the
> result
> of the expression:
>
> (datum->syntax stx 'return)
>
> which means "create a syntax object in the same lexical environment as
> stx,
> and is represented by the symbol 'return'."
>
> - The actual code generation begins within the #`(...) which is a shorthand
> for (quasisyntax (...)) just like '(...) is short for (quote (...)). The
> result of a quasisyntax expression is a syntax object. Basically, it's
> the
> most convenient way of creating a syntax object, but like syntax-rules
> it's
> also hygienic by default and you need to insert "unhygienic" syntax
> objects
> into it explicitly.
>
> - Within the quasisyntax, I use #,ret-id which is short for (unsyntax
> ret-id)
> to inject the unhygienic syntax object that holds the symbol 'return'
> into
> the generated code.
>
> For someone used to macros in the Common Lisp or Elisp style, this may seem
> over-complicated. It's the cost of the "hygienic by default" behavior.
>
> By the way I assume that you're just toying around with the language to
> learn.
> If you were thinking of using a 'def' macro like this in real code, I would
> discourage it because there's already a built-in mechanism that allows the
> programmer something very similar, called 'let/ec':
>
> (import (ice-9 control))
>
> (define (test x)
> (let/ec return
> (cond
> ((= x 3) (return 7))
> ((= x 2) (return 5)))))
>
> As you see it allows you to define a "return" keyword anywhere, and it
> doesn't need to be part of a function definition, it can appear anywhere.
>
> --
> Taylan
>