Re: [racket-dev] Enhancement to the syntax system?
On 07/10/2012 05:03 PM, Matthew Flatt wrote: At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: 20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two stx names into x *or* if I change the argument name for the macro to x, then it returns 2. 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) IIUC then you're saying that also all the following (fx #'x) and (fy #'x) are equivalent to #`(let ((x 0)) x), but if you run this code then you will get a 0 result only for (myy), contrary to what I would expect based on the above explanation. #lang racket (define-for-syntax (fx x) #`(let ((x 0)) #,x)) (define-for-syntax (fy y) #`(let ((x 0)) #,y)) (define-syntax (mxx x) (syntax-case x () ((_) #`(let ((x 99)) #,(fx #'x) (define-syntax (mxy x) (syntax-case x () ((_) #`(let ((x 99)) #,(fy #'x) (define-syntax (myx y) (syntax-case y () ((_) #`(let ((x 99)) #,(fx #'x) (define-syntax (myy y) (syntax-case y () ((_) #`(let ((x 99)) #,(fy #'x) (mxx) (mxy) (myx) (myy) == 99 99 99 0 Marijn
Re: [racket-dev] Enhancement to the syntax system?
20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two stx names into x *or* if I change the argument name for the macro to x, then it returns 2. -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life!
Re: [racket-dev] Enhancement to the syntax system?
Hi, Matthew Flatt mfl...@cs.utah.edu skribis: 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. [...] 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. Interesting. Thanks for the clarification and examples. Ludo’.
Re: [racket-dev] Enhancement to the syntax system?
samth made a pointer to http://srfi.schemers.org/srfi-72/srfi-72.html It does not look like guile racket etc. have implemented this yet. Am I wrong? This is precisely what I'm after! On Tue, Jul 10, 2012 at 5:26 PM, Ludovic Courtès l...@gnu.org wrote: Hi, Matthew Flatt mfl...@cs.utah.edu skribis: 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. [...] 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. Interesting. Thanks for the clarification and examples. Ludo’.
Re: [racket-dev] Enhancement to the syntax system?
At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: 20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two stx names into x *or* if I change the argument name for the macro to x, then it returns 2. 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.
Re: [racket-dev] Enhancement to the syntax system?
On 07/10/2012 10:51 AM, Eli Barzilay wrote: 20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two stx names into x *or* if I change the argument name for the macro to x, then it returns 2. 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*) 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). 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. Ryan
Re: [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!