[racket] define-syntax-rule/id

From: Jon Rafkind (rafkind at cs.utah.edu)
Date: Mon Apr 18 14:42:35 EDT 2011

Here is an alternative implementation that makes the example you showed
at the very bottom of your emails work. I don't know if its "better" but
using `syntax-local-eval' looks somewhat dangerous to me.

This is mostly straight-forward except that to get the right lexical
scope for `get-x1' we need to do a little hack (by assuming the argument
list has at least one thing in it). Of course the original macro could
enforce such a property as well.

#lang racket

(define-syntax (define-syntax-rule/id stx)
  (syntax-case stx ()
    [(_ (name arg ...)
        [id id-rename]
        body ...)
     (let ()
     #'(define-syntax (name stx)
         (syntax-case stx ()
           [(_ arg ...)
            ;; we want the defined procedure to have the same lexical
scope as
            ;; the arguments given to the syntax-rule
            ;; we can't use #'(arg ...) as the thing to get the lexical
scope
            ;; because #' will create a new syntax object using the lexical
            ;; scope of the code right here. so instead we take out the
first
            ;; object from the argument list which should have the right
scope.
            (with-syntax ([id (datum->syntax (car (syntax->list #'(arg
...)))
                                             (string->symbol
                                               (let ([arg (syntax-e
#'arg)] ...)
                                                 id-rename))
                                             (car (syntax->list #'(arg
...))))])
              #'(begin body ...))]))
       )]))

(define-syntax-rule/id (make-getter xid)
                       [id2 (format "get-~a" xid)]
                       (define (id2) xid))

(define x1 5)
(make-getter x1)
(get-x1)


On 04/18/2011 12:03 PM, Laurent wrote:
> Found!
>
> After having read more carefully Eli's very good post
> (http://blog.racket-lang.org/2011/04/writing-syntax-case-macros.html),
> it became clearer that the problem was that the context of the created
> identifier was wrong.
> Deconstructing and reconstructing the id syntax object did the trick:
>
> (define-syntax (define-syntax-rule/id stx)
>   (syntax-case stx ()
>     [(_ [id id-gen] body)
>      (with-syntax ([id-def (to-syntax
>                             (syntax-e (syntax-local-eval #'id-gen))
>                             #:stx stx)])
>        #'(splicing-let-syntax ([tmp-form (syntax-rules ()
>                                   [(_ id) (begin (displayln 'body)
>                                                  body)])])
>             (tmp-form id-def))
>        )]
>     ))
>
>
> Now if anyone knows a better way to implement this or can point out
> problems with this implementation, I'd be glad to know about it.
>
> Thanks for listening :)
> Laurent
>
>
> On Mon, Apr 18, 2011 at 18:21, Laurent <laurent.orseau at gmail.com
> <mailto:laurent.orseau at gmail.com>> wrote:
>
>     Dear Racket list,
>
>     Once again, I need a little help on a macro definition.
>
>     I want to define the following (simplified[1]) macro:
>     (define-syntax-rule/id [/id id-gen/] /body/)
>
>     where id is replaced with the result of id-gen inside body.
>     For example:
>     (define-syntax-rule/id
>       [x #'foo]
>       (begin (define x 5)
>              (displayln x)))
>
>     is supposed to bind foo to 5 in the top-level at run-time.
>     (You can replace #'foo by some complex identifier-making
>     expression using format-id.)
>     Here is what I have right now, after much trial&error:
>
>     #lang racket
>
>     (require (for-syntax unstable/syntax)
>              (for-syntax errortrace/errortrace-key)
>              racket/splicing
>              )
>
>     (provide (all-defined-out))
>
>     ;; Like define-syntax-rule,
>     ;; but id is replaced with the result of id-gen in body.
>     ;; id-gen is eval'ed at macro-time.
>     (define-syntax (define-syntax-rule/id stx)
>       (syntax-case stx ()
>         [(_ [id id-gen] body)
>          (with-syntax ([id-def (syntax-local-eval #'id-gen)])
>            #'(splicing-let-syntax ([tmp-form (syntax-rules ()
>                                       [(_ id) (begin (displayln 'body)
>                                                      body)])])
>                 (tmp-form id-def))
>            )]
>         ))
>
>     ;;; TESTS
>
>     (define-syntax-rule/id
>       [x #'foo]
>       (begin (define x 5)
>              (displayln x)
>              (displayln 'x)
>              ))
>
>
>     It works (foo is bound to 5), except that foo is unknown
>     (undefined identifier) after the (begin ...).
>     I suspect this is because of syntax-local-eval, but I don't know
>     what to use instead.
>
>     There is another intriguing thing:
>     if just after (define x 5) we add (provide x), and in another file
>     we require the first file, then foo is actually defined and can be
>     used normally!
>     Why is that and why does this not work when there is no (provide
>     x) ? [note that there is a (provide (all-defined out)).]
>
>     Can someone give me a hint as to what is going on?
>
>     I also suspect that using splicing-let-syntax is not the way to
>     go, but again I don't know what to use otherwise, that would let
>     me replace id with id-def.
>
>     Thank you very much,
>     Laurent
>
>
>     [1] To explain the name: once finished, this macro will be
>     supposed to behave like an augmented version of define-syntax-rule
>     but with explicit hygiene breaking:
>
>     (define-syntax-rule/id (make-getter id)
>       [id2 (format-id #'here "get-~a" id)]
>       (define (id2) id))
>
>     which, when called on (make-getter foo) would create the get-foo
>     function.
>
>
>
> _________________________________________________
>   For list-related administrative tasks:
>   http://lists.racket-lang.org/listinfo/users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20110418/09550e5a/attachment.html>

Posted on the users mailing list.