[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 ?
- Re: return macro,
adriano <=