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

Reply via email to