Wednesday, February 23, 2005

Abstracting over second-class language forms

Macros are good for abstracting over second-class language forms, which can't be done with ordinary mechanisms like functional abstraction or inheritance. But people are often used to scanning for literal occurrences of these second-class forms in order to understand a program; I noticed this when Sam was trying to read my code and couldn't figure out at first where a name I'd exported had come from, because I'd written a quick macro that took a bunch of names and defined and provided them all in the same way.

Stylistically, one approach to mitigate this is to keep the name as close to the code that defines it as possible. Here's a macro version of for-each that allows you to place an anonymous transformer right near the list of expression arguments it will receive:
(define-syntax syntax-for-each
(syntax-rules ()
[(_ transformer (arg ...))
(begin
(define-syntax anonymous transformer)
(anonymous arg)
...)]))

(syntax-for-each (syntax-rules ()
[(_ name)
(begin
(define name #|whatever|#)
(provide name))]
(foo bar baz bat razzle frizzle fratz))
At least if you were doing a text search for the identifier name, it would show up immediately below the code that defines it.

Incidentally, notice the use of the internal define-syntax. This is necessary, because if you use let-syntax, the code inside of it is no longer at the top level, so any definitions produced by the macro would be treated as internal defines. Yuck.

No comments: