guile-user
[Top][All Lists]
Advanced

[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
>


reply via email to

[Prev in Thread] Current Thread [Next in Thread]