defmacrois pure CL code with the difference that it gets executed at compile-time, Scheme's
syntax-rulesis a specialized sub-language which is almost-but-not-quite Scheme. Therefore, it loses the convenient equivalence to normal Scheme code and is, at least for me, much harder to understand and much less intuitive to use. On the other hand, CL's
defmacrosuffers from the lack of macro hygiene, as opposed to Scheme's macro system. Simple examples of this symptom are easy to fix. Consider this macro (from Practical Common Lisp) - iterating over prime numbers in a given range:
(defmacro do-primes ((var start end) &body; body) `(do ((,var (next-prime ,start) (next-prime (1+ ,var))) (ending-value ,end)) ((> ,var ending-value)) ,@body))The problem here is with
ending-value. This code is broken:
(do-primes (ending-value 0 10) (print ending-value))Because
ending-valueis being re-binded inside
do-primes. Fortunately, this case is easy to fix:
(defmacro do-primes ((var start end) &body; body) (let ((ending-value-name (gensym))) `(do ((,var (next-prime ,start) (next-prime (1+ ,var))) (,ending-value-name ,end)) ((> ,var ,ending-value-name)) ,@body)))
However, there are cases that can't be fixed in any simple way. For example,
look at the last code sample again - what if
do-primes is called
inside some context where
do is redefined to be a different
labels). Sure, this is a far
fetched example - but it can happen, and the code will surely break. Think about
it - the macro can be used successfully for years, until it gets use in a
slightly weird piece of code - and then it will fail in a mysterious way which
is quite difficult to debug.
Another far-fetched-but-can-happen problem: if a macro refers to some global symbol and is called inside a lexical context that shadows this symbol. Another hard-to-fix problem.
There are probably other examples of where
defmacro can break.
Sure, with a judicious use of
gensyms and solid coding conventions
the probability of something happening is low, and indeed, tons of CL code with
macros work perfectly. However, there's this nagging feeling of a looming
disaster that may strike at some unknown time with a slightly non-standard piece
of code. This isn't pleasant.
Scheme's macro system is hygienic and doesn't suffer from these problems. Of course, this comes at the price of simplicity and ease-of-use, as I mentioned earlier.
P.S. Interestingly, you can write code with
defmacro in Scheme if
you want. Most of the major implementations support it. It appears that Scheme's
macros can be used to implement (non-hygienic) CL macros, but not the other way