[racket] define-syntax comment

From: Neil Toronto (neil.toronto at gmail.com)
Date: Thu Mar 6 20:54:26 EST 2014

On 03/06/2014 06:15 PM, Jon Stenerson wrote:
> When I run this in DrRacket, the first (m) gives 0 and the second gives
> #<procedure:+>.
> #lang racket
> (m)
> (define-syntax m (lambda (stx) #'+))
> (m)
> Seems a little strange to me. I see why it happens but I guess I had
> expected a "use before definition" warning.

This one's fun! I know you know why it happens, but to explain to others:

The first (m) expands to (#%app m). The `m' isn't expanded because it's 
not defined as a syntax transformer yet. But it isn't transformed into 
(#%top . m) because `m' is bound in the body of the module.

Then (define-syntax m ...) is expanded. Then (m), which expands to #'+. 
Now that all the module-level expressions are expanded, the expander 
starts on their subexpressions, so the first `m' within (m) on line 2 is 
expanded, and is of course transformed to #'+. The program ends up

   (define-syntax m ...)

with each non-definition wrapped in a `print-values', so of course the 
values 0 and + are printed. Neet.

The way to deal with it is write the syntax transformer so it 
pattern-matches on how it's called (as the head of a larger expression 
or not) and expands appropriately:

#lang racket


(define-syntax (m stx)
   (syntax-case stx ()
     [(_ . es)  (syntax/loc stx (+ . es))]))


This raises a syntax error on the first (m) because the inner `m' fails 
to match.

I don't know whether there's any sensible warning Racket can give for 
the original macro. I'll let the workers of deeper black magic answer 
that one.

Neil ⊥

Posted on the users mailing list.