Other Special Operators

The four remaining special operators, **LOCALLY**, **THE**, **LOAD-TIME-VALUE**, and **PROGV**, all allow you to get at parts of the underlying language that can’t be accessed any other way. **LOCALLY** and **THE** are part of Common Lisp’s declaration system, which is used to communicate things to the compiler that don’t affect the meaning of your code but that may help the compiler generate better code—faster, clearer error messages, and so on.15 I’ll discuss declarations briefly in Chapter 32.

The other two, **LOAD-TIME-VALUE** and **PROGV**, are infrequently used, and explaining the reason why you might ever want to use them would take longer than explaining what they do. So I’ll just tell you what they do so you know they’re there. Someday you’ll hit on one of those rare times when they’re just the thing, and then you’ll be ready.

**LOAD-TIME-VALUE** is used, as its name suggests, to create a value that’s determined at load time. When the file compiler compiles code that contains a **LOAD-TIME-VALUE** form, it arranges to evaluate the first subform once, when the FASL is loaded, and for the code containing the **LOAD-TIME-VALUE** form to refer to that value. In other words, instead of writing this:

  1. (defvar *loaded-at* (get-universal-time))
  2. (defun when-loaded () *loaded-at*)

you can write the following:

  1. (defun when-loaded () (load-time-value (get-universal-time)))

In code not processed by **COMPILE-FILE**, **LOAD-TIME-VALUE** is evaluated once when the code is compiled, which may be when you explicitly compile a function with **COMPILE** or earlier because of implicit compilation performed by the implementation in the course of evaluating the code. In uncompiled code, **LOAD-TIME-VALUE** evaluates its form each time it’s evaluated.

Finally, **PROGV** creates new dynamic bindings for variables whose names are determined at runtime. This is mostly useful for implementing embedded interpreters for languages with dynamically scoped variables. The basic skeleton is as follows:

  1. (progv symbols-list values-list
  2. body-form*)

where symbols-list is a form that evaluates to a list of symbols and values-list is a form that evaluates to a list of values. Each symbol is dynamically bound to the corresponding value, and then the body-forms are evaluated. The difference between **PROGV** and **LET** is that because symbols-list is evaluated at runtime, the names of the variables to bind can be determined dynamically. As I say, this isn’t something you need to do often.

And that’s it for special operators. In the next chapter, I’ll get back to hard-nosed practical topics and show you how to use Common Lisp’s package system to take control of your namespaces so you can write libraries and applications that can coexist without stomping on each other’s names.


1Of course, if **IF** wasn’t a special operator but some other conditional form, such as **COND**, was, you could build **IF** as a macro. Indeed, in many Lisp dialects, starting with McCarthy’s original Lisp, **COND** was the primitive conditional evaluation operator.

2Well, technically those constructs could also expand into a **LAMBDA** expression since, as I mentioned in Chapter 6, **LET** could be defined—and was in some earlier Lisps—as a macro that expands into an invocation of an anonymous function.

3Surprising as it may seem, it actually is possible to make anonymous functions recurse. However, you must use a rather esoteric mechanism known as the Y combinator. But the Y combinator is an interesting theoretical result, not a practical programming tool, so is well outside the scope of this book.

4It’s not required that **WITH-SLOTS** be implemented with **SYMBOL-MACROLET**--in some implementations, **WITH-SLOTS** may walk the code provided and generate an expansion with x, y, and z already replaced with the appropriate **SLOT-VALUE** forms. You can see how your implementation does it by evaluating this form:

  1. (macroexpand-1 '(with-slots (x y z) obj (list x y z)))

However, walking the body is much easier for the Lisp implementation to do than for user code; to replace x, y, and z only when they appear in value positions requires a code walker that understands the syntax of all special operators and that recursively expands all macro forms in order to determine whether their expansions include the symbols in value positions. The Lisp implementation obviously has such a code walker at its disposal, but it’s one of the few parts of Lisp that’s not exposed to users of the language.

5One version of f2cl is available as part of the Common Lisp Open Code Collection (CLOCC): http://clocc.sourceforge.net/. By contrast, consider the tricks the authors of f2j, a FORTRAN-to-Java translator, have to play. Although the Java Virtual Machine (JVM) has a goto instruction, it’s not directly exposed in Java. So to compile FORTRAN gotos, they first compile the FORTRAN code into legal Java source with calls to a dummy class to represent the labels and gotos. Then they compile the source with a regular Java compiler and postprocess the byte codes to translate the dummy calls into JVM-level byte codes. Clever, but what a pain.

6Since this algorithm depends on values returned by **RANDOM**, you may want to test it with a consistent random seed, which you can get by binding ***RANDOM-STATE*** to the value of (make-random-state nil) around each call to algorithm-s. For instance, you can do a basic sanity check of algorithm-s by evaluating this:

  1. (let ((*random-state* (make-random-state nil))) (algorithm-s 10 200))

If your refactorings are all valid, this expression should evaluate to the same list each time.

7This is a pretty reasonable restriction—it’s not entirely clear what it’d mean to return from a form that has already returned—unless, of course, you’re a Scheme programmer. Scheme supports continuations, a language construct that makes it possible to return from the same function call more than once. But for a variety of reasons, few, if any, languages other than Scheme support this kind of continuation.

8If you’re the kind of person who likes to know how things work all the way down to the bits, it may be instructive to think about how you might implement the condition system’s macros using **BLOCK**, **TAGBODY**, closures, and dynamic variables.

9**UNWIND-PROTECT** is essentially equivalent to try/finally constructs in Java and Python.

10And indeed, CLSQL, the multi-Lisp, multidatabase SQL interface library, provides a similar macro called with-database. CLSQL’s home page is at http://clsql.b9.com.

11A small handful of macros don’t pass through extra return values of the forms they evaluate. In particular, the **PROG1** macro, which evaluates a number of forms like a **PROGN** before returning the value of the first form, returns that form’s primary value only. Likewise, **PROG2**, which returns the value of the second of its subforms, returns only the primary value. The special operator **MULTIPLE-VALUE-PROG1** is a variant of **PROG1** that returns all the values returned by the first form. It’s a minor wart that **PROG1** doesn’t already behave like **MULTIPLE-VALUE-PROG1**, but neither is used often enough that it matters much. The **OR** and **COND** macros are also not always transparent to multiple values, returning only the primary value of certain subforms.

12The reason loading a file with an **IN-PACKAGE** form in it has no effect on the value of ***PACKAGE*** after **LOAD** returns is because **LOAD** binds ***PACKAGE*** to its current value before doing anything else. In other words, something equivalent to the following **LET** is wrapped around the rest of the code in **LOAD**:

  1. (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.

13In some implementations, you may be able to get away with evaluating **DEFUN**s 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 **LOAD**ing the definitions from source, not when compiling with **COMPILE-FILE**, so in general macro definitions must be evaluated before they’re used.

14By contrast, the subforms in a top-level **LET** aren’t compiled as top-level forms because they’re not run directly when the FASL is loaded. They will run, but it’s in the runtime context of the bindings established by the **LET**. Theoretically, a **LET** that binds no variables could be treated like a **PROGN**, but it’s not—the forms appearing in a **LET** are never treated as top-level forms.

15The one declaration that has an effect on the semantics of a program is the **SPECIAL** declaration mentioned in Chapter 6.