Common Lisp programming is often presented as “interactive”. In most languages, modifications to your program are applied by recompiling it and restarting it. In contrast, Common Lisp lets you incrementally modify your program while it is running.
While this approach is convenient, especially for exploratory programming, it also means that the state of your program during execution does not always reflect the source code. You do not just define new constructs: you look them up, inspect them, modify them or delete them. I had to learn a lot of subtleties the hard way. This article is a compendium of information related to the interactive nature of Common Lisp.
In Common Lisp variables are identified by symbols. Evaluating
(SETQ A 42)
creates or updates a variable with the integer
42 as value, and associates
it to the
A symbol. After the call to
(BOUNDP 'A) will return
(SYMBOL-VALUE 'A) will return
You do not delete a variable: instead, you remove the association between the
symbol and the variable. You do so with
MAKUNBOUND. Following the previous
(MAKUNBOUND 'A) will remove the association between the
and the variable. And
(BOUNDP 'A) returns
NIL as expected. As for
(SYMBOL-VALUE 'A), it now signals an
UNBOUND-VARIABLE error as mandated by
DEFPARAMETER? They are also used to declare
variables (globally defined ones), associating them with symbols. Both define
“special” variables (i.e. variables for which all bindings are dynamic; see
CLtL2 9.2). The difference is that the initial value passed to
DEFVAR is not
evaluated if it already has a value.
MAKUNBOUND will work on variables
DEFPARAMETER as expected.
DEFCONSTANT is a bit more complicated. CLtL21 5.3.2 states that “once a
name has been declared by defconstant to be constant, any further assignment
to or binding of that special variable is an error”, but does not clearly
MAKUNBOUND should or should not be able to be used on
constants. However, CLtL2 5.3.2 also states that “defconstant […] does assert
that the value of the variable name is fixed and does license the compiler to
build assumptions about the value into programs being compiled”. If the
compiler is allowed to rely on the value associated with the variable name, it
would make sense not to allow the deletion of the binding. Thus it is
recommended to only use constants for values that are guaranteed to never
change, e.g. mathematical constants. Most of the time you want
MAKUNBOUND does not apply to lexical variables.
Common Lisp is a Lisp-2, meaning that variables and functions are part of two separate namespaces. Despite this clear separation, functions behave similarly to variables.
DEFUN will either create or update the global function associated with
SYMBOL-FUNCTION returns the globally defined function associated
with a symbol, and
FMAKUNBOUND deletes this association.
Let us point out a common mistake when referencing functions:
'F) yields a symbol while
(FUNCTION F) (abbreviated as
#'F) yields a function. The function argument of
be either a symbol or a function (see CLtL2 7.3) It has two consequences:
First, one can write a function referencing
(QUOTE F) with the
F will later be bound to a function. The following function
definition is perfectly valid even though
F has not been defined yet:
(defun foo (a b) (funcall 'f a b))
Second, redefining the
F function will update its association (or binding)
F symbol, but the previous function will still be available if it has
been referenced somewhere before the update. For example:
(setf (symbol-function 'foo) #'1+) (let ((old-foo #'foo)) (setf (symbol-function 'foo) #'1-) (funcall old-foo 42))
What about macros? Since macros are a specific kind of functions (CLtL2 5.1.4
“a macro is essentially a function from forms to forms”), it is not surprising
that they share the same namespace and can be manipulated in the same way as
Symbols and packages
While functions and variables are familiar concepts to developers, Common Lisp symbols and packages are a bit more peculiar.
A symbol is interned when it is part of a package. The most explicit way to
create an interned symbol is to use
interns the symbol in the current package by default, but one can pass a
package as second argument. After that,
(FIND-SYMBOL "FOO") will return our
interned symbol as expected.
More surprisingly, the reader automatically interns symbols. You can test it
(READ-FROM-STRING "BAR"). After evaluation,
BAR is a symbol
interned in the current package. This also means that it is very easy to
pollute a package with symbols in ways you did not necessarily expect. To
clean up, simply use
UNINTERN. Remember to refer to the right symbol: to
remove the symbol
BAR from the package
(UNINTERN 'FOO::BAR "BAR").
A symbol is either internal or external.
EXPORT will make a symbol external
to its package while
UNEXPORT will make it internal. As for
confusion usually arises around which symbol is affected.
(UNEXPORT 'FOO:BAR "FOO") correctly refers to the external symbol in the
FOO package and makes
it internal again.
(UNEXPORT 'BAR "FOO") will signal an error since the
BAR symbol is not part of the
FOO package (unless of course the current
package happens to be
Packages themselves can be created with
MAKE-PACKAGE and destroyed with
DELETE-PACKAGE. Developers are usually more familiar with
macro allowing the creation of a package and its configuration (package use
list, imported and exported symbols, etc.) in a declarative way. A surprising
and frustrating behavior is that evaluating a
DEFPACKAGE form for a package
that already exists will result in undefined behavior if the new declaration
“is not consistent” (CLtL2 11.7) with the current state of the package. As an
example, adding symbols to the export list is perfectly fine. Removing one
will result in undefined behavior (usually an error) due to the inconsistency
of the export list. Fortunately, Common Lisp offers all the necessary
functions to manipulate packages and their symbols: use them!
The Common Lisp standard includes CLOS, the Common Lisp Object System. Unsurprisingly it provides multiple ways to interact with classes and objects dynamically.
As variables or functions, classes are identified by symbols and
returns the class associated with a symbol. Class names are part of a separate
namespace shared with structures and types.
DEFCLASS macro is the only way to define or redefine a class. Redefining
a class means that instances created afterward with
MAKE-INSTANCE will use
the new definition. Existing instances are updated: newly added slots are
added (either unbound or using the value associated with
slots that are not defined anymore are deleted.
UPDATE-INSTANCE-FOR-REDEFINED-CLASS is particularly interesting: developers
can define methods for this generic function in order to control how instances
are updated when their class is redefined.
Defining classes may imply implicitly defining methods: the
:WRITER slot keyword arguments will lead to the creation of
generic functions. When a class is redefined, methods associated with slots
that have been removed will live on.
A limitation of CLOS is that classes cannot be deleted.
FIND-CLASS can be
used as a place, and
(SETF (FIND-CLASS 'FOO) NIL) will remove the
association between the
FOO symbol and the class, but the class itself and
its instances will not disappear. While this limitation may seem strange, ask
yourself how an implementation should handle instances of a class that has
The class of an instance can be changed with
CHANGE-CLASS: slots that exist
in the new class will be conserved while those that do not are deleted. New
slots are either unbound or set to the value associated with
the new class. In a way similar to
UPDATE-INSTANCE-FOR-DIFFERENT-CLASS lets developers control precisely the
Generics and methods
Generics are functions which can be specialized based on the class (and not type as one could expect) of their arguments and which can have a method combination type.
Generics can be created explicitly with
DEFGENERIC or implicitly when
DEFMETHOD is called and the list of parameter specializers and method
combination does not match any existing generic function. Since generics are
FMAKUNBOUND will work as
Methods themselves are either defined as part of the
DEFGENERIC call or
DEFMETHOD. Discovering the different methods associated with
a generic function is a bit more complicated. There is no standard way to list
the methods associated with a generic, but it is at least possible to look up
a method with
FIND-METHOD. Do remember to pass a function (and not a symbol)
as the generic, and to pass classes (and not symbols naming classes) in the
list of specializers.
Redefinition is not as obvious as for non-generic functions. When redefining a
DEFGENERIC all methods defined as part of the previous
DEFGENERIC form are removed and methods defined in the redefinition are
added. However, methods defined separately with
DEFMETHOD are not affected.
For example, in the following code, the second call to
replace the two methods specialized on
FLOAT respectively by a
single one specialized on a
STREAM, but the method specialized on
will remain unaffected.
(defgeneric foo (a) (:method ((a integer)) (format nil "~A is an integer" a)) (:method ((a float)) (format nil "~A is a float" a))) (defmethod foo ((a string)) (format nil "~S is a string" a)) (defgeneric foo (a) (:method ((a stream)) (format nil "~A is a stream" a)))
Note that trying to redefine a generic with a different parameter lambda list will cause the removal of all previously defined methods since none of them can match the new parameters.
Removing a method will require you to find it first using
REMOVE-METHOD. With the previous example, removing the method
specialized on a
STRING argument is done with:
(remove-method #'foo (find-method #'foo nil (list (find-class 'string)) nil))
Working with methods is not always easy, and two errors are very common.
First, remember that changing the combinator in a
DEFMETHOD will define a
new method. If you realize that your
:AFTER method should use
DEFMETHOD form, remember to delete the method with the
:AFTER combinator or you will end up with two methods being called.
Second, when defining a method for a generic from another package, remember to
correctly refer to the generic. If you want to define a method on the
generic from package
(DEFMETHOD FOO:BAR (…) …) and not
(DEFMETHOD BAR (…) …). In the latter case, you will define a new
generic in the current package.
Meta Object Protocol
While CLOS is already quite powerful, various interactions are impossible. One cannot create classes or methods programmatically, introspect classes or instances for example to list their slots or obtain all their superclasses, or list all the methods associated with a generic function.
In addition of an example of a CLOS implementation, The Art of the Metaobject Protocol2 defines multiple extensions to CLOS including metaclasses, metaobjects, dynamic class and generic creation, class introspection and much more.
Most Common Lisp implementations implement at least part of these extensions, usually abbreviated as “MOP”, for “MetaObject Protocol”. The well-known closer-mop system can be used as a compatibility layer for multiple implementations.
Structures are record constructs defined with
DEFSTRUCT. At a glance they
may seem very similar to classes, but they have a fundamental limitation:
the results of redefining a structure are undefined (CLtL2 19.2).
While this property allows implementations to handle structures in a more efficient way than classes, it makes structures unsuitable for incremental development. As such, they should only be used as a last resort, when a regular class has been proved to be a performance bottleneck.
While conditions look very similar to classes the Common Lisp standard does not define them as classes. This is one of the few differences between the standard and CLtL2 which clearly states in 29.3.4 that “Common Lisp condition types are in fact CLOS classes, and condition objects are ordinary CLOS objects”.
This is why one uses
DEFINE-CONDITION instead of
MAKE-CONDITION instead of
MAKE-INSTANCE. This also means that one should
not use slot-related functions (including the very useful
In practice, most modern implementations follow CLtL2 and the
CLOS-CONDITIONS:INTEGRATE X3J13 Cleanup
and implement conditions as CLOS classes, meaning that conditions can be
manipulated and redefined as any other classes. And the same way as any other
classes, they cannot be deleted.
Types are identified by symbols and are part of the same namespace as classes (which should not be surprising since defining a class automatically defines a type with the same name).
Types are defined with
DEFTYPE, but documentation is surprisingly silent on
the effects of type redefinition. This can lead to interesting situations. On
some implementations (e.g. SBCL and CCL), if a class slot is defined as having
FOO will not be taken into account and the type
checking operation (which is not mandated by the standard) will use the
previous definition of the type. Infortunatly Common Lisp does not mandate
any specific behavior on slot type mismatches (CLtL2 18.104.22.168).
Thus developers should not expect any useful effect from redefining types. Restarting the implementation after substantial type changes is probably best.
In the same vein interactions with types are very limited. You cannot find a
type by its symbol or even check whether a type exists or not. Calling
TYPE-OF on a value will return a type this value satisfies, but the nature
of the type is implementation-dependent (CLtL2 4.9): it could be any
supertype. In other words,
TYPE-OF could absolutly return
T for all values
NIL. At least
SUBTYPE-P lets you check whether a type is a subtype of
Common Lisp is a complex language with a lot of subtleties, way more than what can be covered in a blog post. The curious reader will probably skip the standard (not because you have to buy it, but because it is a low quality scan of a printed document and jump directly to CLtL2 or the Common Lisp HyperSpec. The Art of the Metaobject Protocol is of course the normative reference for the CLOS extensions usually referred to as “MOP”.