The traditional way to define macros in Lisp is very similar to procedure
definitions. The key differences are that the macro definition body should
return a list that describes the transformed expression, and that the definition
is marked as a macro definition (rather than a procedure definition) by the use
of a different definition keyword: in Lisp, defmacro
rather than
defun
, and in Scheme, define-macro
rather than define
.
Guile supports this style of macro definition using both defmacro
and define-macro
. The only difference between them is how the
macro name and arguments are grouped together in the definition:
(defmacro name (args ...) body ...)
is the same as
(define-macro (name args ...) body ...)
The difference is analogous to the corresponding difference between
Lisp’s defun
and Scheme’s define
.
Having read the previous section on syntax-case
, it’s probably clear that
Guile actually implements defmacros in terms of syntax-case
, applying the
transformer on the expression between invocations of syntax->datum
and
datum->syntax
. This realization leads us to the problem with defmacros,
that they do not preserve referential transparency. One can be careful to not
introduce bindings into expanded code, via liberal use of gensym
, but
there is no getting around the lack of referential transparency for free
bindings in the macro itself.
Even a macro as simple as our when
from before is difficult to get right:
(define-macro (when cond exp . rest) `(if ,cond (begin ,exp . ,rest))) (when #f (display "Launching missiles!\n")) ⇒ #f (let ((if list)) (when #f (display "Launching missiles!\n"))) -| Launching missiles! ⇒ (#f #<unspecified>)
Guile’s perspective is that defmacros have had a good run, but that modern
macros should be written with syntax-rules
or syntax-case
. There
are still many uses of defmacros within Guile itself, but we will be phasing
them out over time. Of course we won’t take away defmacro
or
define-macro
themselves, as there is lots of code out there that uses
them.