[racket-dev] Enhancement to the syntax system?
An hour and a half ago, Matthew Flatt wrote:
>
> It's natural --- but not correct --- to think that #` is responsible
> for hygiene, in which case `(f #'x)' should keep the given `x'
> separate from the `let'-bound `x' in the result.
>
> Instead, hygiene is the responsibility of macro invocation, and
>
> #`(let ([x 1]) #,#'x)
>
> is simply the same as
>
> #`(let ([x 1]) x)
>
> and so
>
> (f #'x)
>
> above is equivalent to
>
> #`(let ([x 1]) x)
>
>
> If you change the example to
>
> #lang racket
> (begin-for-syntax
> (define-syntax-rule (f body)
> #`(let ([x 1]) body)))
> (define-syntax (m stx)
> (with-syntax ([zz (f x)]) #`(let ([x 2]) zz)))
> (m)
>
> so that `f' is used as a macro instead of a function, then you get 2,
> since the macro-expansion of `(f x)' keeps the `x's separate.
Yeah, I think that this kind of confusion is there, but it's easy (at
least "relatively easy") to build a mental model of how things work
and avoid such problems -- but the tricky bit here is that things
break when the `stx' is renamed to `x'.
50 minutes ago, Ryan Culpepper wrote:
>
> The difference between
>
> (define-for-syntax (f1 stx) #`(let ([x 1]) #,stx)
>
> and
>
> (define-for-syntax (f2 x) #`(let ([x 1]) #,x)
>
> is that the 'x' in the template in the 'f2' is not bound-identifier=? to
> the 'x' that appears in the template of 'm', because it has a rename
> wrap due to the 'x' formal parameter of 'f2'. That is, 'f2' behaves
> essentially the same as
>
> (define-for-syntax (f2* x*) #`(let ([x* 1]) #,x*)
Yeah, I think that this is the more confusing bit. (I suspected
something along this line, but didn't have time to figure out a good
explanation...) So I think that the real confusion is actually a
combination of what Matthew pointed at in the above and this one,
making the result trickier than both. In other words, I *think* that
the effect of the transformer's argument name makes it looks like the
#` *was* responsible for hygiene when it's actually just this point
that makes it happen... (This is all "IIUC".)
> A likely mistake is to think that the wrap generated by the 'x' in
> 'f2' doesn't count because it happens before we get around to the
> "real" macro expansion that you care about. But that's not the case
> (at least in Racket).
>
> A good rule of thumb is to never use local variable names in your
> macro implementation (including compile-time auxiliary functions)
> that also appear in the macro's template (including etc).
Yeah, and a subtle lesson here is that `stx' is a useful convention.
(I think that `x' is common in some guile code, so this would be a
point for them.)
> A related error is the "identifier used out of context" error that
> you get from, eg,
>
> (define-syntax (m stx)
> (let ([+ *])
> #'(+ 1 2)))
> (m) ;; => identifier used out of context in: +
>
> The binding of '+' in the macro changes the meaning of the '+' in
> the template, even though the bindings exist at different
> phases. You could perhaps avoid this issue by changing the hygiene
> algorithm by adding a phase level to rename wraps and skipping
> different-phase rename wraps during resolution. I'm not sure if this
> is a good idea or if anyone has tried it.
(And this is what Matthew's last example gets by changing `f' to a
macro, right? Also, Stefan posted a related message to the
scheme-reports list where he imlpemented some new #. thing which is
(roughly speaking) something that expands to a `let-syntax' and
therefore tries to do the same.)
In any case, it would be nice if the original example I posted (which
is a variant of what was discussed originally) would throw an error
instead of looking right in a way that is wrong...
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!