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! _________________________ Racket Developers list: http://lists.racket-lang.org/dev