[racket] define-syntax-rule/id

From: Laurent (laurent.orseau at gmail.com)
Date: Tue Apr 19 02:48:28 EDT 2011

Whoa, very nice!

IIUC, you could get rid of syntax-local-eval because you have a sub-macro,
so the overall macro puts the content of id-rename inside a sub-macro that
is then evaluated.
This is also why id is correctly "bound" inside (begin body ...) : after the
first expansion, it is not really body ..., but the full expression
containing id.
It is definitely better.

Also, the first let () is not really needed I guess.

Thank you very much.
Laurent

On Mon, Apr 18, 2011 at 20:42, Jon Rafkind <rafkind at cs.utah.edu> wrote:

>  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> 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
>
>
>
> _________________________________________________
>  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/20110419/4a3fdcdf/attachment.html>

Posted on the users mailing list.