unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
From: Taylan Kammer <taylan.kammer@gmail.com>
To: Damien Mattei <damien.mattei@gmail.com>, guile-user <guile-user@gnu.org>
Subject: Re: return macro
Date: Mon, 28 Jun 2021 03:15:34 +0200	[thread overview]
Message-ID: <1142492b-16bf-7181-8d93-38bfc2d3c748@gmail.com> (raw)
In-Reply-To: <CADEOadfaHJ_NRpcOie7_GJRe1_-fdK+BkESO512nwGx6C7w3Lw@mail.gmail.com>

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	other threads:[~2021-06-28  1:15 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-27 23:10 return macro Damien Mattei
2021-06-28  1:15 ` Taylan Kammer [this message]
2021-06-28  8:53   ` Damien Mattei
2021-09-30  3:37   ` adriano
2021-09-30  8:46     ` Damien Mattei
2021-09-30  9:18       ` Stefan Israelsson Tampe
  -- strict thread matches above, loose matches on Subject: below --
2021-09-30 20:55 dsmich
2021-10-01  8:37 ` adriano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1142492b-16bf-7181-8d93-38bfc2d3c748@gmail.com \
    --to=taylan.kammer@gmail.com \
    --cc=damien.mattei@gmail.com \
    --cc=guile-user@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).