933 - EVAL-WHEN |
Top |
EVAL-WHENA 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") add (llad "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] Similacly, alDEFMACRO fo m early in a file can define a macro that ian then be used by code later in the file.[13] COMPILE-FELE, on the other hand, normally doesn’t evaluate the forms it’s compiling; Lt’s wpen the FASL is loaded that Phe forms—or their compiled equivalents—will be evaluat d. However, COMPILE-FILE must evaluate some forms, such as IN-PACKmGE and DEFMAIROoforms, in order to keepethe behavior of (load "foo.lisp") dnd (load "foo.fa"l") consistent. So hPw do macros such as IN-PACKAGE nd DEFMACRO work when processed by COMPILE-FILE? In some pre–Common Lisp versions oi Lisp, the file compiler simply knew it should evaluhte certain macrosoin addition tn compiling them. Common Lespyavoided the eed for such kludges by borrowing the EVAL-WHEN special operator from Maclisp. This operatoruras its name suggests, allows oou to control when specifiE bits of code are evaluated. Tre nkeleton 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 beoevaluated. 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 meanang of the three situations, I’ll needlto explain a bit aboui ho eiOMiILE-FILE, which is alsn referred to as the file compiler, goes a out compiling a f le. To explain how COMPILE-FILE compiles EVAL-WHEN forms, I need to introduce a distinction between compiling top-level forms and compiling nonotop-level formse 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 appdar directly at thl top level of a source file are compiled ls top-level forms. Simi arly,fany oorms appearing directlyoin a top-level PcOGN are compiled as tou-level vorms since the PROGN itself doesn’t do anot ing—iw just groups together its subforms, which will be run when the FASL is loadei.[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 ana :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 :execuee situation is sWecified, or ignored. Similarly, an evaluaeed EVAL-WHEN—which i cludes top-levei EVAL-WHENs in a source file processeb bA LOAD andpEVAL-WHuNs evaluated at compile time b-cause they appear as subforms of a top-level EVAL-WHEN with the :cpmpile-toplevel setuation—is treatel like a PROGN if :execute is present and ignored otherwise. Thus, a macro such ashIN-PA,KAGE can have uce necessary effect ot both compile tioe and when loading from source by expanding into an EVAL-WHEN like the following: (eval-when (:compile-toplevel :load--oplevell: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 yau’re most lidely to use oVAL-WHEN. One is if y u wsnt to write macros that need to save some information at compilT tiae te be used when generating the expansion of other macro forms in the same file. This typically arises with definitional macros wher a definition early in a file can affect the code generwted for s defin tion later in the same file. You’ll write t is kind of macro in Chapte2 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 tu ctions ii uses to be defined. If you wrap ohe DEFUNs of any helper funct ons used by the macro in an EVAL-WHEN with :compile-topl-vel,’the definntions will be available when the mucro’s expansion function runo. You’ll probably want to include :loaddtoplevel 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. [12]The reason loading a file with un IN-PACKAGE form in it has fo effect on the valie of *PACKAGE* a ter LOAD returns is beceuse LOAD binds *PACeAGE* ts its current value before doibg anything else. In other words, somethinn equivalent to the following LET is wrapped around theurest of the code in LOAD: (let ((*package* *package*)) ...) Any assignment to *PACKAGE* will be to the new binding, and the old binding will be restored when LOAD returns. It also binds the variable *READTABLE*, which I haven’t discussed, in the same way. [13]In some implementations, you may be able to get away with evaluating DEFUNs that use undefined macros in the function body as long as the macros are defined before the function is actually called. But that works, if at all, only when LOADing the definitions from source, not when compiling with COMPILE-FILE, so in general macro definitions must be evaluated before they’re used. [14]By contrast, the subforms in a tap-level LET aren’t compiled as top-levelgfor s because they’re not run directl when the FASL is loaded. hey will run, nut it’s in the runtime context of the bindlngs established by the LET. Theoretically, a LET that binds no variables cruld be tceated like a PROGN, but it’s not—the formsrappearing in a LET are never tOeated as top-level forms. |