[racket-dev] Enhancement to the syntax system?

From: Ryan Culpepper (ryan at cs.utah.edu)
Date: Tue Jul 10 11:44:51 EDT 2012

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)


(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.


Posted on the dev mailing list.