[racket] macro calling itself
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