Macro Expansion Time vs. Runtime
The key to understanding macros is to be quite clear about the distinction between the code that generates code (macros) and the code that eventually makes up the program (everything else). When you write macros, you’re writing programs that will be used by the compiler to generate the code that will then be compiled. Only after all the macros have been fully expanded and the resulting code compiled can the program actually be run. The time when macros run is called macro expansion time; this is distinct from runtime, when regular code, including the code generated by macros, runs.
It’s important to keep this distinction firmly in mind because code running at macro expansion time runs in a very different environment than code running at runtime. Namely, at macro expansion time, there’s no way to access the data that will exist at runtime. Like Mac, who couldn’t run the programs he was working on because he didn’t know what the correct inputs were, code running at macro expansion time can deal only with the data that’s inherent in the source code. For instance, suppose the following source code appears somewhere in a program:
(defun foo (x)
(when (> x 10) (print 'big)))
Normally you’d think of x
as a variable that will hold the argument passed in a call to foo
. But at macro expansion time, such as when the compiler is running the **WHEN**
macro, the only data available is the source code. Since the program isn’t running yet, there’s no call to foo
and thus no value associated with x
. Instead, the values the compiler passes to **WHEN**
are the Lisp lists representing the source code, namely, (> x 10)
and (print 'big)
. Suppose that **WHEN**
is defined, as you saw in the previous chapter, with something like the following macro:
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body)))
When the code in foo
is compiled, the **WHEN**
macro will be run with those two forms as arguments. The parameter condition
will be bound to the form (> x 10)
, and the form (print 'big)
will be collected into a list that will become the value of the **&rest**
body
parameter. The backquote expression will then generate this code:
(if (> x 10) (progn (print 'big)))
by interpolating in the value of condition
and splicing the value of body
into the **PROGN**
.
When Lisp is interpreted, rather than compiled, the distinction between macro expansion time and runtime is less clear because they’re temporally intertwined. Also, the language standard doesn’t specify exactly how an interpreter must handle macros—it could expand all the macros in the form being interpreted and then interpret the resulting code, or it could start right in on interpreting the form and expand macros when it hits them. In either case, macros are always passed the unevaluated Lisp objects representing the subforms of the macro form, and the job of the macro is still to produce code that will do something rather than to do anything directly.