1 min read

Syntax Brain Twister

I gave this brain twister at the Scheme Workshop 2010, but I think it's a neat thing so I'll give it here as well. This has been previously discussed on IRC, but I think it deserves a blog post.

Consider the following program:

(let ()
  (define a 1)
  (define-syntax (m x)
    (syntax-case x ()
      [(_ a) #'(y here)]))
  (define-syntax (y x)
    (syntax-case x ()
      [(_ k) (datum->syntax #'k 'a)]))
  (m a))

What does this code evaluate to? No really, I want you to take a look at it and tell me what you think. Take your time now, give it a shot.

Got it? Chances are you said that this should evaluate to 1. Right? Well, hehe, wrong. Yes, I know, you have the idea right, but you probably had a fleeting consideration about why the 'a' in the m macro defintion was never used. You thought it was useless, didn't you? Well, it isn't. That 'a' is actually a syntax variable. So what? Syntax variables occupy the same namespace as normal variables in Scheme, which means that they affect the environment visible from the wraps place on the 'here' identifier, which is later used to wrap the new 'a' in the y macro. All this means that when the 'a' is wrapped in the y macro, it's not actually pointing to the (DEFINE A 1) 'a', but it's actually pointing to the syntax variable, which will lead to an identifier out of context error. Now see what happens when you replace (_ a) with (_ ba). Fun, no?

So remember, syntax variables occupy the variable namespace and can affect bindings.