[racket] macro calling itself

From: Matthew Butterick (mb at mbtype.com)
Date: Mon Oct 27 17:04:43 EDT 2014

Yes, that works, thank you. I didn't know that one could use something
other than the usual `stx` inside `format-id`.

On Mon, Oct 27, 2014 at 1:24 PM, Matthew Flatt <mflatt at cs.utah.edu> wrote:

> We sometimes call this the "non-hygienic macros compose poorly"
> problem.
>
> The two-subform case of `definer` uses the overall form, `stx`, to
> provide a content for the introduced `arg-id` binding. So, when you use
> the two-subform variant directly in some context, then you can see the
> `arg-id` binding in that same context.
>
> When you use the one-subform `definer`, it expands to a two-subform
> `definer`, but that new `definer` form is macro-introduced.
> Consequently, further expansion also marks `arg-id` as
> macro-introduced, and so `arg-id` becomes inaccessible from the context
> that used the one-subform `definer`.
>
>
> In this case, using the given `id` as the lexical context for the
> synthesized name (instead of the overall `definer` form) is almost
> certainly what you want:
>
>   (format-id #'id "~a-~a" #'arg #'id)
>
> That makes `zam-baz-id` visible in the same contexts as `baz-id`. It's
> still a non-hygienic macro in some sense, but it's an ok kind, because
> the context for the introduced binding comes from a sensible part of
> the macro use.
>
>
> At Mon, 27 Oct 2014 13:10:39 -0700, Matthew Butterick wrote:
> > I'm unclear why I'm getting an unbound-identifier error in this macro.
> I've
> > written other self-referencing macros in the past that have worked, so
> I'm
> > unclear why this one doesn't.
> >
> > What I expect is that when `definer` is called with one argument, the
> first
> > branch of `syntax-case` will stick on a default argument and call
> `definer`
> > again. Which it does, except that in that case, it only binds the name
> 'id'
> > rather than both 'id' and 'arg-id'.
> >
> >
> > #lang racket
> > (require (for-syntax racket/syntax) rackunit)
> >
> > (define-syntax (definer stx)
> >   (syntax-case stx ()
> >     [(_ id)
> >      #'(definer id zam)]
> >     [(_ id arg)
> >      (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])
> >        #'(begin
> >            (define id (quote id))
> >            (define arg-id (quote arg-id))))]))
> >
> >
> > (definer foo-id bar)
> > (check-equal? foo-id 'foo-id)
> > (check-equal? bar-foo-id 'bar-foo-id)
> >
> > (definer baz-id)
> > (check-equal? baz-id 'baz-id)
> > (check-equal? zam-baz-id 'zam-baz-id) ;; unbound identifier error
> >
> >
> > OTOH, this version of the macro, which repeats the expansion template
> rather
> > than calling itself, works fine:
> >
> >
> > #lang racket
> > (require (for-syntax racket/syntax) rackunit)
> >
> > (define-syntax (definer2 stx)
> >   (syntax-case stx ()
> >     [(_ id)
> >      (with-syntax ([arg-id (format-id stx "~a-~a" (format-id stx "zam")
> #'id)])
> >        #'(begin
> >            (define id (quote id))
> >            (define arg-id (quote arg-id))))]
> >     [(_ id arg)
> >      (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])
> >        #'(begin
> >            (define id (quote id))
> >            (define arg-id (quote arg-id))))]))
> >
> >
> > (definer2 foo-id bar)
> > (check-equal? foo-id 'foo-id)
> > (check-equal? bar-foo-id 'bar-foo-id)
> >
> > (definer2 baz-id)
> > (check-equal? baz-id 'baz-id)
> > (check-equal? zam-baz-id 'zam-baz-id) ;; no error this time
> > ____________________
> >   Racket Users list:
> >   http://lists.racket-lang.org/users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20141027/91a9a9cf/attachment-0001.html>

Posted on the users mailing list.