<div dir="ltr">Yes, that works, thank you. I didn't know that one could use something other than the usual `stx` inside `format-id`. </div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Oct 27, 2014 at 1:24 PM, Matthew Flatt <span dir="ltr"><<a href="mailto:mflatt@cs.utah.edu" target="_blank">mflatt@cs.utah.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">We sometimes call this the "non-hygienic macros compose poorly"<br>
problem.<br>
<br>
The two-subform case of `definer` uses the overall form, `stx`, to<br>
provide a content for the introduced `arg-id` binding. So, when you use<br>
the two-subform variant directly in some context, then you can see the<br>
`arg-id` binding in that same context.<br>
<br>
When you use the one-subform `definer`, it expands to a two-subform<br>
`definer`, but that new `definer` form is macro-introduced.<br>
Consequently, further expansion also marks `arg-id` as<br>
macro-introduced, and so `arg-id` becomes inaccessible from the context<br>
that used the one-subform `definer`.<br>
<br>
<br>
In this case, using the given `id` as the lexical context for the<br>
synthesized name (instead of the overall `definer` form) is almost<br>
certainly what you want:<br>
<br>
  (format-id #'id "~a-~a" #'arg #'id)<br>
<br>
That makes `zam-baz-id` visible in the same contexts as `baz-id`. It's<br>
still a non-hygienic macro in some sense, but it's an ok kind, because<br>
the context for the introduced binding comes from a sensible part of<br>
the macro use.<br>
<div><div class="h5"><br>
<br>
At Mon, 27 Oct 2014 13:10:39 -0700, Matthew Butterick wrote:<br>
> I'm unclear why I'm getting an unbound-identifier error in this macro. I've<br>
> written other self-referencing macros in the past that have worked, so I'm<br>
> unclear why this one doesn't.<br>
><br>
> What I expect is that when `definer` is called with one argument, the first<br>
> branch of `syntax-case` will stick on a default argument and call `definer`<br>
> again. Which it does, except that in that case, it only binds the name 'id'<br>
> rather than both 'id' and 'arg-id'.<br>
><br>
><br>
> #lang racket<br>
> (require (for-syntax racket/syntax) rackunit)<br>
><br>
> (define-syntax (definer stx)<br>
>   (syntax-case stx ()<br>
>     [(_ id)<br>
>      #'(definer id zam)]<br>
>     [(_ id arg)<br>
>      (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])<br>
>        #'(begin<br>
>            (define id (quote id))<br>
>            (define arg-id (quote arg-id))))]))<br>
><br>
><br>
> (definer foo-id bar)<br>
> (check-equal? foo-id 'foo-id)<br>
> (check-equal? bar-foo-id 'bar-foo-id)<br>
><br>
> (definer baz-id)<br>
> (check-equal? baz-id 'baz-id)<br>
> (check-equal? zam-baz-id 'zam-baz-id) ;; unbound identifier error<br>
><br>
><br>
> OTOH, this version of the macro, which repeats the expansion template rather<br>
> than calling itself, works fine:<br>
><br>
><br>
> #lang racket<br>
> (require (for-syntax racket/syntax) rackunit)<br>
><br>
> (define-syntax (definer2 stx)<br>
>   (syntax-case stx ()<br>
>     [(_ id)<br>
>      (with-syntax ([arg-id (format-id stx "~a-~a" (format-id stx "zam") #'id)])<br>
>        #'(begin<br>
>            (define id (quote id))<br>
>            (define arg-id (quote arg-id))))]<br>
>     [(_ id arg)<br>
>      (with-syntax ([arg-id (format-id stx "~a-~a" #'arg #'id)])<br>
>        #'(begin<br>
>            (define id (quote id))<br>
>            (define arg-id (quote arg-id))))]))<br>
><br>
><br>
> (definer2 foo-id bar)<br>
> (check-equal? foo-id 'foo-id)<br>
> (check-equal? bar-foo-id 'bar-foo-id)<br>
><br>
> (definer2 baz-id)<br>
> (check-equal? baz-id 'baz-id)<br>
> (check-equal? zam-baz-id 'zam-baz-id) ;; no error this time<br>
</div></div>> ____________________<br>
>   Racket Users list:<br>
>   <a href="http://lists.racket-lang.org/users" target="_blank">http://lists.racket-lang.org/users</a><br>
</blockquote></div><br></div>