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 ____________________ Racket Users list: http://lists.racket-lang.org/users