guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: return macro


From: adriano
Subject: Re: return macro
Date: Thu, 30 Sep 2021 05:37:35 +0200

Il giorno lun, 28/06/2021 alle 03.15 +0200, Taylan Kammer ha scritto:
> 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)))))



What does this return (defined with let/ec) do ?

In the orevious versions I could see the call to call/cc so I could
(somewhat) figure out the "jump" imlpied by calling return

But in this last case, where is the return behaviour defined ?





reply via email to

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