EVAL-WHEN
A special operator you’ll need to understand in order to write certain kinds of macros is **EVAL-WHEN**
. For some reason, Lisp books often treat **EVAL-WHEN**
as a wizards-only topic. But the only prerequisite to understanding **EVAL-WHEN**
is an understanding of how the two functions **LOAD**
and **COMPILE-FILE**
interact. And understanding **EVAL-WHEN**
will be important as you start writing certain kinds of more sophisticated macros, such as the ones you’ll write in Chapters 24 and 31.
I’ve touched briefly on the relation between **LOAD**
and **COMPILE-FILE**
in previous chapters, but it’s worth reviewing again here. The job of **LOAD**
is to load a file and evaluate all the top-level forms it contains. The job of **COMPILE-FILE**
is to compile a source file into a FASL file, which can then be loaded with **LOAD**
such that (load "foo.lisp")
and (load "foo.fasl")
are essentially equivalent.
Because **LOAD**
evaluates each form before reading the next, the side effects of evaluating forms earlier in the file can affect how forms later in the form are read and evaluated. For instance, evaluating an **IN-PACKAGE**
form changes the value of ***PACKAGE***
, which will affect the way subsequent forms are read.12 Similarly, a **DEFMACRO**
form early in a file can define a macro that can then be used by code later in the file.13
**COMPILE-FILE**
, on the other hand, normally doesn’t evaluate the forms it’s compiling; it’s when the FASL is loaded that the forms—or their compiled equivalents—will be evaluated. However, **COMPILE-FILE**
must evaluate some forms, such as **IN-PACKAGE**
and **DEFMACRO**
forms, in order to keep the behavior of (load "foo.lisp")
and (load "foo.fasl")
consistent.
So how do macros such as **IN-PACKAGE**
and **DEFMACRO**
work when processed by **COMPILE-FILE**
? In some pre-Common Lisp versions of Lisp, the file compiler simply knew it should evaluate certain macros in addition to compiling them. Common Lisp avoided the need for such kludges by borrowing the **EVAL-WHEN**
special operator from Maclisp. This operator, as its name suggests, allows you to control when specific bits of code are evaluated. The skeleton of an **EVAL-WHEN**
form looks like this:
(eval-when (situation*)
body-form*)
There are three possible situations--:compile-toplevel
, :load-toplevel
, and :execute
--and which ones you specify controls when the body-forms will be evaluated. An **EVAL-WHEN**
with multiple situations is equivalent to several **EVAL-WHEN**
forms, one per situation, each with the same body code. To explain the meaning of the three situations, I’ll need to explain a bit about how **COMPILE-FILE**
, which is also referred to as the file compiler, goes about compiling a file.
To explain how **COMPILE-FILE**
compiles **EVAL-WHEN**
forms, I need to introduce a distinction between compiling top-level forms and compiling non-top-level forms. A top-level form is, roughly speaking, one that will be compiled into code that will be run when the FASL is loaded. Thus, all forms that appear directly at the top level of a source file are compiled as top-level forms. Similarly, any forms appearing directly in a top-level **PROGN**
are compiled as top-level forms since the **PROGN**
itself doesn’t do anything—it just groups together its subforms, which will be run when the FASL is loaded.14 Similarly, forms appearing directly in a **MACROLET**
or **SYMBOL-MACROLET**
are compiled as top-level forms because after the compiler has expanded the local macros or symbol macros, there will be no remnant of the **MACROLET**
or **SYMBOL-MACROLET**
in the compiled code. Finally, the expansion of a top-level macro form will be compiled as a top-level form.
Thus, a **DEFUN**
appearing at the top level of a source file is a top-level form—the code that defines the function and associates it with its name will run when the FASL is loaded—but the forms within the body of the function, which won’t run until the function is called, aren’t top-level forms. Most forms are compiled the same when compiled as top-level and non-top-level forms, but the semantics of an **EVAL-WHEN**
depend on whether it’s being compiled as a top- level form, compiled as a non-top-level form, or simply evaluated, combined with what situations are listed in its situation list.
The situations :compile-toplevel
and :load-toplevel
control the meaning of an **EVAL-WHEN**
compiled as a top-level form. When :compile-toplevel
is present, the file compiler will evaluate the subforms at compile time. When :load-toplevel
is present, it will compile the subforms as top-level forms. If neither of these situations is present in a top-level **EVAL-WHEN**
, the compiler ignores it.
When an **EVAL-WHEN**
is compiled as a non-top-level form, it’s either compiled like a **PROGN**
, if the :execute
situation is specified, or ignored. Similarly, an evaluated **EVAL-WHEN**
--which includes top-level **EVAL-WHEN**
s in a source file processed by **LOAD**
and **EVAL-WHEN**
s evaluated at compile time because they appear as subforms of a top-level **EVAL-WHEN**
with the :compile-toplevel
situation—is treated like a **PROGN**
if :execute
is present and ignored otherwise.
Thus, a macro such as **IN-PACKAGE**
can have the necessary effect at both compile time and when loading from source by expanding into an **EVAL-WHEN**
like the following:
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf *package* (find-package "PACKAGE-NAME")))
***PACKAGE***
will be set at compile time because of the :compile-toplevel
situation, set when the FASL is loaded because of :load-toplevel
, and set when the source is loaded because of the :execute
.
There are two ways you’re most likely to use **EVAL-WHEN**
. One is if you want to write macros that need to save some information at compile time to be used when generating the expansion of other macro forms in the same file. This typically arises with definitional macros where a definition early in a file can affect the code generated for a definition later in the same file. You’ll write this kind of macro in Chapter 24.
The other time you might need **EVAL-WHEN**
is if you want to put the definition of a macro and helper functions it uses in the same file as code that uses the macro. **DEFMACRO**
already includes an **EVAL-WHEN**
in its expansion so the macro definition is immediately available to be used later in the file. But **DEFUN**
normally doesn’t make function definitions available at compile time. But if you use a macro in the same file as it’s defined in, you need the macro and any functions it uses to be defined. If you wrap the **DEFUN**
s of any helper functions used by the macro in an **EVAL-WHEN**
with :compile-toplevel
, the definitions will be available when the macro’s expansion function runs. You’ll probably want to include :load-toplevel
and :execute
as well since the macros will also need the function definitions after the file is compiled and loaded or if you load the source instead of compiling.