Syntax-Rules
2010-12-11
Introduction
The Revised(5)
Report on the Algorithmic Language Scheme specifies hygienic macros
using the syntax-rules
mechanism. These macros are called
&ldquot;hygienic&rdquot;, because they do not allow for variable
capture the way defmacro
macros do. The general consensus
is that this makes them less powerful, but also less tricky to
write.
Syntax-rules macros, being in the standard, are supported by all up to date Scheme implementations, although some of them may require extensions to be installed or loaded (e.g. SCM needs to be started with -r macro).
Examples
Syntax-rules macros are defined in a way similar to
functions, using define-syntax
or let-syntax
to bind them to a name (analogous to define
or
let
), and syntax-rules
to actually define the
macro (analogous to lambda
). However, the macro expansion
is very different from function evaluation, as it is based on pattern
matching. A few simple examples:
; simply return what was passed in
(define-syntax id
(syntax-rules ()
((id x) x)))
; Do something twice
(define-syntax twice
(syntax-rules ()
((twice x) (begin x x))))
So how does syntax-rules
work? Basically,
it takes a list of clauses, each of which contains a pattern followed by
a list. If the pattern matches, the list is returned. The patterns above
consist of two symbols, the first of which would match the name of the
macro, and the second of which would match the first argument.
It is
important to understand that these variables can have any name we care
to give them, and this bears no relation to any names outside the
syntax-rules
form. This is what makes
syntax-rules
macros hygienic; even if there were an outside
variable named x
, it would not conflict with the
x
s used inside the macros. Also note that naming the first
pattern variable after the macro is just a convention; it is not
enforced at all.
The first argument to syntax-rules
is a list of things
to match literally. Any symbol occurring in that list will not be
treated as a pattern variable, but will have to occur literally in the
input. The following macro is intended to clarify pattern matching and
the use of literal symbols. It emulates cond
using
if
and begin
:
(define-syntax kond
(syntax-rules (else)
((kond (else code ...))
(begin code ...))
((kond (condition code ...))
(if condition (begin code ...)))
((kond (condition code ...) alts ...)
(if condition
(begin code ...)
(kond alts ...)))))
Note that the ...
are actually part of the
pattern language. I hope you understand what it all means. At any
rate, it should be clear that macros allow cond
to be
implemented in terms of if
and begin
.
Similarly, let
can be implemented using
lambda
, and let*
and letrec
using let
, etc. In the end, it turns out that only a
few constructs are fundamental to Scheme, the rest can all be
implemented using functions and macros.