Hubbry Logo
Common LispCommon LispMain
Open search
Common Lisp
Community hub
Common Lisp
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Common Lisp
Common Lisp
from Wikipedia

Common Lisp
ParadigmMulti-paradigm: procedural, functional, object-oriented, meta, reflective, generic
FamilyLisp
Designed byScott Fahlman, Richard P. Gabriel, David A. Moon, Kent Pitman, Guy Steele, Dan Weinreb
DeveloperANSI X3J13 committee
First appeared1984 (41 years ago) (1984), 1994 (31 years ago) (1994) for ANSI Common Lisp
Typing disciplineDynamic, strong
ScopeLexical, optionally dynamic
OSCross-platform
Filename extensions.lisp, .lsp, .l, .cl, .fasl
Websitecommon-lisp.net
Major implementations
Allegro CL, ABCL, Clasp, CLISP, Clozure CL, CMUCL, ECL, GCL, LispWorks, Scieneer CL, SBCL, Symbolics Common Lisp
Dialects
CLtL1, CLtL2, ANSI Common Lisp
Influenced by
Lisp, Lisp Machine Lisp, Maclisp, Scheme, Interlisp
Influenced
Clojure, Dylan, Emacs Lisp, EuLisp, ISLISP, *Lisp, AutoLisp, Julia, Moose, R, SKILL, SubL

Common Lisp (CL) is a dialect of the Lisp programming language, published in American National Standards Institute (ANSI) standard document ANSI INCITS 226-1994 (S2018)[1] (formerly X3.226-1994 (R1999)).[2] The Common Lisp HyperSpec, a hyperlinked HTML version, has been derived from the ANSI Common Lisp standard.[3]

The Common Lisp language was developed as a standardized and improved successor of Maclisp. By the early 1980s several groups were already at work on diverse successors to MacLisp: Lisp Machine Lisp (aka ZetaLisp), Spice Lisp, NIL and S-1 Lisp. Common Lisp sought to unify, standardise, and extend the features of these MacLisp dialects. Common Lisp is not an implementation, but rather a language specification.[4] Several implementations of the Common Lisp standard are available, including free and open-source software and proprietary products.[5] Common Lisp is a general-purpose, multi-paradigm programming language. It supports a combination of procedural, functional, and object-oriented programming paradigms. As a dynamic programming language, it facilitates evolutionary and incremental software development, with iterative compilation into efficient run-time programs. This incremental development is often done interactively without interrupting the running application.

It also supports optional type annotation and casting, which can be added as necessary at the later profiling and optimization stages, to permit the compiler to generate more efficient code. For instance, fixnum can hold an unboxed integer in a range supported by the hardware and implementation, permitting more efficient arithmetic than on big integers or arbitrary precision types. Similarly, the compiler can be told on a per-module or per-function basis which type of safety level is wanted, using optimize declarations.

Common Lisp includes CLOS, an object system that supports multimethods and method combinations. It is often implemented with a Metaobject Protocol.

Common Lisp is extensible through standard features such as Lisp macros (code transformations) and reader macros (input parsers for characters).

Common Lisp provides partial backwards compatibility with Maclisp and John McCarthy's original Lisp. This allows older Lisp software to be ported to Common Lisp.[6]

History

[edit]

Work on Common Lisp started in 1981 after an initiative by ARPA manager Bob Engelmore to develop a single community standard Lisp dialect.[7] Much of the initial language design was done via electronic mail.[8][9] In 1982, Guy L. Steele Jr. gave the first overview of Common Lisp at the 1982 ACM Symposium on LISP and functional programming.[10]

The first language documentation was published in 1984 as Common Lisp the Language (known as CLtL1), first edition. A second edition (known as CLtL2), published in 1990, incorporated many changes to the language, made during the ANSI Common Lisp standardization process: extended LOOP syntax, the Common Lisp Object System, the Condition System for error handling, an interface to the pretty printer and much more. But CLtL2 does not describe the final ANSI Common Lisp standard and thus is not a documentation of ANSI Common Lisp. The final ANSI Common Lisp standard then was published in 1994. Since then no update to the standard has been published. Various extensions and improvements to Common Lisp (examples are Unicode, Concurrency, CLOS-based IO) have been provided by implementations and libraries.

Syntax

[edit]

Common Lisp is a dialect of Lisp. It uses S-expressions to denote both code and data structure. Function calls, macro forms and special forms are written as lists, with the name of the operator first, as in these examples:

 (+ 2 2)           ; adds 2 and 2, yielding 4. The function's name is '+'. Lisp has no operators as such.
 (defvar *x*)      ; Ensures that a variable *x* exists,
                   ; without giving it a value. The asterisks are part of
                   ; the name, by convention denoting a special (global) variable. 
                   ; The symbol *x* is also hereby endowed with the property that
                   ; subsequent bindings of it are dynamic, rather than lexical.
 (setf *x* 42.1)   ; Sets the variable *x* to the floating-point value 42.1
 ;; Define a function that squares a number:
 (defun square (x)
   (* x x))
 ;; Execute the function:
 (square 3)        ; Returns 9
 ;; The 'let' construct creates a scope for local variables. Here
 ;; the variable 'a' is bound to 6 and the variable 'b' is bound
 ;; to 4. Inside the 'let' is a 'body', where the last computed value is returned.
 ;; Here the result of adding a and b is returned from the 'let' expression.
 ;; The variables a and b have lexical scope, unless the symbols have been
 ;; marked as special variables (for instance by a prior DEFVAR).
 (let ((a 6)
       (b 4))
   (+ a b))        ; returns 10

Data types

[edit]

Common Lisp has many data types.

Scalar types

[edit]

Number types include integers, ratios, floating-point numbers, and complex numbers.[11] Common Lisp uses bignums to represent numerical values of arbitrary size and precision. The ratio type represents fractions exactly, a facility not available in many languages. Common Lisp automatically coerces numeric values among these types as appropriate.

The Common Lisp character type is not limited to ASCII characters. Most modern implementations allow Unicode characters.[12]

The symbol type is common to Lisp languages, but largely unknown outside them. A symbol is a unique, named data object with several parts: name, value, function, property list, and package. Of these, value cell and function cell are the most important. Symbols in Lisp are often used similarly to identifiers in other languages: to hold the value of a variable; however there are many other uses. Normally, when a symbol is evaluated, its value is returned. Some symbols evaluate to themselves, for example, all symbols in the keyword package are self-evaluating. Boolean values in Common Lisp are represented by the self-evaluating symbols T and NIL. Common Lisp has namespaces for symbols, called 'packages'.

A number of functions are available for rounding scalar numeric values in various ways. The function round rounds the argument to the nearest integer, with halfway cases rounded to the even integer. The functions truncate, floor, and ceiling round towards zero, down, or up respectively. All these functions return the discarded fractional part as a secondary value. For example, (floor -2.5) yields −3, 0.5; (ceiling -2.5) yields −2, −0.5; (round 2.5) yields 2, 0.5; and (round 3.5) yields 4, −0.5.

Data structures

[edit]

Sequence types in Common Lisp include lists, vectors, bit-vectors, and strings. There are many operations that can work on any sequence type.

As in almost all other Lisp dialects, lists in Common Lisp are composed of conses, sometimes called cons cells or pairs. A cons is a data structure with two slots, called its car and cdr. A list is a linked chain of conses or the empty list. Each cons's car refers to a member of the list (possibly another list). Each cons's cdr refers to the next cons—except for the last cons in a list, whose cdr refers to the nil value. Conses can also easily be used to implement trees and other complex data structures; though it is usually advised to use structure or class instances instead. It is also possible to create circular data structures with conses.

Common Lisp supports multidimensional arrays, and can dynamically resize adjustable arrays if required. Multidimensional arrays can be used for matrix mathematics. A vector is a one-dimensional array. Arrays can carry any type as members (even mixed types in the same array) or can be specialized to contain a specific type of members, as in a vector of bits. Usually, only a few types are supported. Many implementations can optimize array functions when the array used is type-specialized. Two type-specialized array types are standard: a string is a vector of characters, while a bit-vector is a vector of bits.

Hash tables store associations between data objects. Any object may be used as key or value. Hash tables are automatically resized as needed.

Packages are collections of symbols, used chiefly to separate the parts of a program into namespaces. A package may export some symbols, marking them as part of a public interface. Packages can use other packages.

Structures, similar in use to C structs and Pascal records, represent arbitrary complex data structures with any number and type of fields (called slots). Structures allow single-inheritance.

Classes are similar to structures, but offer more dynamic features and multiple-inheritance. (See CLOS). Classes have been added late to Common Lisp and there is some conceptual overlap with structures. Objects created of classes are called Instances. A special case is Generic Functions. Generic Functions are both functions and instances.

Functions

[edit]

Common Lisp supports first-class functions. For instance, it is possible to write functions that take other functions as arguments or return functions as well. This makes it possible to describe very general operations.

The Common Lisp library relies heavily on such higher-order functions. For example, the sort function takes a relational operator as an argument and key function as an optional keyword argument. This can be used not only to sort any type of data, but also to sort data structures according to a key.

 ;; Sorts the list using the > and < function as the relational operator.
 (sort (list 5 2 6 3 1 4) #'>)   ; Returns (6 5 4 3 2 1)
 (sort (list 5 2 6 3 1 4) #'<)   ; Returns (1 2 3 4 5 6)
 ;; Sorts the list according to the first element of each sub-list.
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; Returns ((3 B) (4 C) (9 A))

The evaluation model for functions is very simple. When the evaluator encounters a form (f a1 a2...) then it presumes that the symbol named f is one of the following:

  1. A special operator (easily checked against a fixed list)
  2. A macro operator (must have been defined previously)
  3. The name of a function (default), which may either be a symbol, or a sub-form beginning with the symbol lambda.

If f is the name of a function, then the arguments a1, a2, ..., an are evaluated in left-to-right order, and the function is found and invoked with those values supplied as parameters.

Defining functions

[edit]

The macro defun defines functions where a function definition gives the name of the function, the names of any arguments, and a function body:

 (defun square (x)
   (* x x))

Function definitions may include compiler directives, known as declarations, which provide hints to the compiler about optimization settings or the data types of arguments. They may also include documentation strings (docstrings), which the Lisp system may use to provide interactive documentation:

 (defun square (x)
   "Calculates the square of the single-float x."
   (declare (single-float x) (optimize (speed 3) (debug 0) (safety 1)))
   (the single-float (* x x)))

Anonymous functions (function literals) are defined using lambda expressions, e.g. (lambda (x) (* x x)) for a function that squares its argument. Lisp programming style frequently uses higher-order functions for which it is useful to provide anonymous functions as arguments.

Local functions can be defined with flet and labels.

 (flet ((square (x)
          (* x x)))
   (square 3))

There are several other operators related to the definition and manipulation of functions. For instance, a function may be compiled with the compile operator. (Some Lisp systems run functions using an interpreter by default unless instructed to compile; others compile every function).

Defining generic functions and methods

[edit]

The macro defgeneric defines generic functions. Generic functions are a collection of methods. The macro defmethod defines methods.

Methods can specialize their parameters over CLOS standard classes, system classes, structure classes or individual objects. For many types, there are corresponding system classes.

When a generic function is called, multiple-dispatch will determine the effective method to use.

 (defgeneric add (a b))
 (defmethod add ((a number) (b number))
   (+ a b))
 (defmethod add ((a vector) (b number))
   (map 'vector (lambda (n) (+ n b)) a))
 (defmethod add ((a vector) (b vector))
   (map 'vector #'+ a b))
(defmethod add ((a string) (b string))
  (concatenate 'string a b))
 (add 2 3)                   ; returns 5
 (add #(1 2 3 4) 7)          ; returns #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ; returns #(5 5 5 5)
 (add "COMMON " "LISP")      ; returns "COMMON LISP"

Generic Functions are also a first class data type. There are many more features to Generic Functions and Methods than described above.

The function namespace

[edit]

The namespace for function names is separate from the namespace for data variables. This is a key difference between Common Lisp and Scheme. For Common Lisp, operators that define names in the function namespace include defun, flet, labels, defmethod and defgeneric.

To pass a function by name as an argument to another function, one must use the function special operator, commonly abbreviated as #'. The first sort example above refers to the function named by the symbol > in the function namespace, with the code #'>. Conversely, to call a function passed in such a way, one would use the funcall operator on the argument.

Scheme's evaluation model is simpler: there is only one namespace, and all positions in the form are evaluated (in any order) – not just the arguments. Code written in one dialect is therefore sometimes confusing to programmers more experienced in the other. For instance, many Common Lisp programmers like to use descriptive variable names such as list or string which could cause problems in Scheme, as they would locally shadow function names.

Whether a separate namespace for functions is an advantage is a source of contention in the Lisp community. It is usually referred to as the Lisp-1 vs. Lisp-2 debate. Lisp-1 refers to Scheme's model and Lisp-2 refers to Common Lisp's model. These names were coined in a 1988 paper by Richard P. Gabriel and Kent Pitman, which extensively compares the two approaches.[13]

Multiple return values

[edit]

Common Lisp supports the concept of multiple values,[14] where any expression always has a single primary value, but it might also have any number of secondary values, which might be received and inspected by interested callers. This concept is distinct from returning a list value, as the secondary values are fully optional, and passed via a dedicated side channel. This means that callers may remain entirely unaware of the secondary values being there if they have no need for them, and it makes it convenient to use the mechanism for communicating information that is sometimes useful, but not always necessary. For example,

  • The TRUNCATE function[15] rounds the given number to an integer towards zero. However, it also returns a remainder as a secondary value, making it very easy to determine what value was truncated. It also supports an optional divisor parameter, which can be used to perform Euclidean division trivially:
(let ((x 1266778)
      (y 458))
  (multiple-value-bind (quotient remainder)
      (truncate x y)
    (format nil "~A divided by ~A is ~A remainder ~A" x y quotient remainder)))

;;;; => "1266778 divided by 458 is 2765 remainder 408"
  • GETHASH[16] returns the value of a key in an associative map, or the default value otherwise, and a secondary Boolean indicating whether the value was found. Thus code that does not care about whether the value was found or provided as the default can simply use it as-is, but when such distinction is important, it might inspect the secondary Boolean and react appropriately. Both use cases are supported by the same call and neither is unnecessarily burdened or constrained by the other. Having this feature at the language level removes the need to check for the existence of the key or compare it to null as would be done in other languages.
(defun get-answer (library)
  (gethash 'answer library 42))

(defun the-answer-1 (library)
  (format nil "The answer is ~A" (get-answer library)))
;;;; Returns "The answer is 42" if ANSWER not present in LIBRARY

(defun the-answer-2 (library)
  (multiple-value-bind (answer sure-p)
      (get-answer library)
    (if (not sure-p)
        "I don't know"
     (format nil "The answer is ~A" answer))))
;;;; Returns "I don't know" if ANSWER not present in LIBRARY

Multiple values are supported by a handful of standard forms, most common of which are the MULTIPLE-VALUE-BIND special form for accessing secondary values and VALUES for returning multiple values:

(defun magic-eight-ball ()
  "Return an outlook prediction, with the probability as a secondary value"
  (values "Outlook good" (random 1.0)))

;;;; => "Outlook good"
;;;; => 0.3187

Other types

[edit]

Other data types in Common Lisp include:

  • Pathnames represent files and directories in the filesystem. The Common Lisp pathname facility is more general than most operating systems' file naming conventions, making Lisp programs' access to files broadly portable across diverse systems.
  • Input and output streams represent sources and sinks of binary or textual data, such as the terminal or open files.
  • Common Lisp has a built-in pseudo-random number generator (PRNG). Random state objects represent reusable sources of pseudo-random numbers, allowing the user to seed the PRNG or cause it to replay a sequence.
  • Conditions are a type used to represent errors, exceptions, and other "interesting" events to which a program may respond.
  • Classes are first-class objects, and are themselves instances of classes called metaobject classes (metaclasses for short).
  • Readtables are a type of object which control how Common Lisp's reader parses the text of source code. By controlling which readtable is in use when code is read in, the programmer can change or extend the language's syntax.

Scope

[edit]

Like programs in many other programming languages, Common Lisp programs make use of names to refer to variables, functions, and many other kinds of entities. Named references are subject to scope.

The association between a name and the entity which the name refers to is called a binding.

Scope refers to the set of circumstances in which a name is determined to have a particular binding.

Determiners of scope

[edit]

The circumstances which determine scope in Common Lisp include:

  • the location of a reference within an expression. If it's the leftmost position of a compound, it refers to a special operator or a macro or function binding, otherwise to a variable binding or something else.
  • the kind of expression in which the reference takes place. For instance, (go x) means transfer control to label x, whereas (print x) refers to the variable x. Both scopes of x can be active in the same region of program text, since tagbody labels are in a separate namespace from variable names. A special form or macro form has complete control over the meanings of all symbols in its syntax. For instance, in (defclass x (a b) ()), a class definition, the (a b) is a list of base classes, so these names are looked up in the space of class names, and x isn't a reference to an existing binding, but the name of a new class being derived from a and b. These facts emerge purely from the semantics of defclass. The only generic fact about this expression is that defclass refers to a macro binding; everything else is up to defclass.
  • the location of the reference within the program text. For instance, if a reference to variable x is enclosed in a binding construct such as a let which defines a binding for x, then the reference is in the scope created by that binding.
  • for a variable reference, whether or not a variable symbol has been, locally or globally, declared special. This determines whether the reference is resolved within a lexical environment, or within a dynamic environment.
  • the specific instance of the environment in which the reference is resolved. An environment is a run-time dictionary which maps symbols to bindings. Each kind of reference uses its own kind of environment. References to lexical variables are resolved in a lexical environment, et cetera. More than one environment can be associated with the same reference. For instance, thanks to recursion or the use of multiple threads, multiple activations of the same function can exist at the same time. These activations share the same program text, but each has its own lexical environment instance.

To understand what a symbol refers to, the Common Lisp programmer must know what kind of reference is being expressed, what kind of scope it uses if it is a variable reference (dynamic versus lexical scope), and also the run-time situation: in what environment is the reference resolved, where was the binding introduced into the environment, et cetera.

Kinds of environment

[edit]

Global

[edit]

Some environments in Lisp are globally pervasive. For instance, if a new type is defined, it is known everywhere thereafter. References to that type look it up in this global environment.

Dynamic

[edit]

One type of environment in Common Lisp is the dynamic environment. Bindings established in this environment have dynamic extent, which means that a binding is established at the start of the execution of some construct, such as a let block, and disappears when that construct finishes executing: its lifetime is tied to the dynamic activation and deactivation of a block. However, a dynamic binding is not just visible within that block; it is also visible to all functions invoked from that block. This type of visibility is known as indefinite scope. Bindings which exhibit dynamic extent (lifetime tied to the activation and deactivation of a block) and indefinite scope (visible to all functions which are called from that block) are said to have dynamic scope.

Common Lisp has support for dynamically scoped variables, which are also called special variables. Certain other kinds of bindings are necessarily dynamically scoped also, such as restarts and catch tags. Function bindings cannot be dynamically scoped using flet (which only provides lexically scoped function bindings), but function objects (a first-level object in Common Lisp) can be assigned to dynamically scoped variables, bound using let in dynamic scope, then called using funcall or APPLY.

Dynamic scope is extremely useful because it adds referential clarity and discipline to global variables. Global variables are frowned upon in computer science as potential sources of error, because they can give rise to ad-hoc, covert channels of communication among modules that lead to unwanted, surprising interactions.

In Common Lisp, a special variable which has only a top-level binding behaves just like a global variable in other programming languages. A new value can be stored into it, and that value simply replaces what is in the top-level binding. Careless replacement of the value of a global variable is at the heart of bugs caused by the use of global variables. However, another way to work with a special variable is to give it a new, local binding within an expression. This is sometimes referred to as "rebinding" the variable. Binding a dynamically scoped variable temporarily creates a new memory location for that variable, and associates the name with that location. While that binding is in effect, all references to that variable refer to the new binding; the previous binding is hidden. When execution of the binding expression terminates, the temporary memory location is gone, and the old binding is revealed, with the original value intact. Of course, multiple dynamic bindings for the same variable can be nested.

In Common Lisp implementations which support multithreading, dynamic scopes are specific to each thread of execution. Thus special variables serve as an abstraction for thread local storage. If one thread rebinds a special variable, this rebinding has no effect on that variable in other threads. The value stored in a binding can only be retrieved by the thread which created that binding. If each thread binds some special variable *x*, then *x* behaves like thread-local storage. Among threads which do not rebind *x*, it behaves like an ordinary global: all of these threads refer to the same top-level binding of *x*.

Dynamic variables can be used to extend the execution context with additional context information which is implicitly passed from function to function without having to appear as an extra function parameter. This is especially useful when the control transfer has to pass through layers of unrelated code, which simply cannot be extended with extra parameters to pass the additional data. A situation like this usually calls for a global variable. That global variable must be saved and restored, so that the scheme doesn't break under recursion: dynamic variable rebinding takes care of this. And that variable must be made thread-local (or else a big mutex must be used) so the scheme doesn't break under threads: dynamic scope implementations can take care of this also.

In the Common Lisp library, there are many standard special variables. For instance, all standard I/O streams are stored in the top-level bindings of well-known special variables. The standard output stream is stored in *standard-output*.

Suppose a function foo writes to standard output:

  (defun foo ()
    (format t "Hello, world"))

To capture its output in a character string, *standard-output* can be bound to a string stream and called:

  (with-output-to-string (*standard-output*)
    (foo))
 -> "Hello, world" ; gathered output returned as a string

Lexical

[edit]

Common Lisp supports lexical environments. Formally, the bindings in a lexical environment have lexical scope and may have either an indefinite extent or dynamic extent, depending on the type of namespace. Lexical scope means that visibility is physically restricted to the block in which the binding is established. References which are not textually (i.e. lexically) embedded in that block simply do not see that binding.

The tags in a TAGBODY have lexical scope. The expression (GO X) is erroneous if it is not embedded in a TAGBODY which contains a label X. However, the label bindings disappear when the TAGBODY terminates its execution, because they have dynamic extent. If that block of code is re-entered by the invocation of a lexical closure, it is invalid for the body of that closure to try to transfer control to a tag via GO:

  (defvar *stashed*) ;; will hold a function

  (tagbody
    (setf *stashed* (lambda () (go some-label)))
    (go end-label) ;; skip the (print "Hello")
   some-label
    (print "Hello")
   end-label)
  -> NIL

When the TAGBODY is executed, it first evaluates the setf form which stores a function in the special variable *stashed*. Then the (go end-label) transfers control to end-label, skipping the code (print "Hello"). Since end-label is at the end of the tagbody, the tagbody terminates, yielding NIL. Suppose that the previously remembered function is now called:

  (funcall *stashed*) ;; Error!

This situation is erroneous. One implementation's response is an error condition containing the message, "GO: tagbody for tag SOME-LABEL has already been left". The function tried to evaluate (go some-label), which is lexically embedded in the tagbody, and resolves to the label. However, the tagbody isn't executing (its extent has ended), and so the control transfer cannot take place.

Local function bindings in Lisp have lexical scope, and variable bindings also have lexical scope by default. By contrast with GO labels, both of these have indefinite extent. When a lexical function or variable binding is established, that binding continues to exist for as long as references to it are possible, even after the construct which established that binding has terminated. References to lexical variables and functions after the termination of their establishing construct are possible thanks to lexical closures.

Lexical binding is the default binding mode for Common Lisp variables. For an individual symbol, it can be switched to dynamic scope, either by a local declaration, by a global declaration. The latter may occur implicitly through the use of a construct like DEFVAR or DEFPARAMETER. It is an important convention in Common Lisp programming that special (i.e. dynamically scoped) variables have names which begin and end with an asterisk sigil * in what is called the "earmuff convention".[17] If adhered to, this convention effectively creates a separate namespace for special variables, so that variables intended to be lexical are not accidentally made special.

Lexical scope is useful for several reasons.

Firstly, references to variables and functions can be compiled to efficient machine code, because the run-time environment structure is relatively simple. In many cases it can be optimized to stack storage, so opening and closing lexical scopes has minimal overhead. Even in cases where full closures must be generated, access to the closure's environment is still efficient; typically each variable becomes an offset into a vector of bindings, and so a variable reference becomes a simple load or store instruction with a base-plus-offset addressing mode.

Secondly, lexical scope (combined with indefinite extent) gives rise to the lexical closure, which in turn creates a whole paradigm of programming centered around the use of functions being first-class objects, which is at the root of functional programming.

Thirdly, perhaps most importantly, even if lexical closures are not exploited, the use of lexical scope isolates program modules from unwanted interactions. Due to their restricted visibility, lexical variables are private. If one module A binds a lexical variable X, and calls another module B, references to X in B will not accidentally resolve to the X bound in A. B simply has no access to X. For situations in which disciplined interactions through a variable are desirable, Common Lisp provides special variables. Special variables allow for a module A to set up a binding for a variable X which is visible to another module B, called from A. Being able to do this is an advantage, and being able to prevent it from happening is also an advantage; consequently, Common Lisp supports both lexical and dynamic scope.

Macros

[edit]

A macro in Lisp superficially resembles a function in usage. However, rather than representing an expression which is evaluated, it represents a transformation of the program source code. The macro gets the source it surrounds as arguments, binds them to its parameters and computes a new source form. This new form can also use a macro. The macro expansion is repeated until the new source form does not use a macro. The final computed form is the source code executed at runtime.

Typical uses of macros in Lisp:

  • new control structures (example: looping constructs, branching constructs)
  • scoping and binding constructs
  • simplified syntax for complex and repeated source code
  • top-level defining forms with compile-time side-effects
  • data-driven programming
  • embedded domain specific languages (examples: SQL, HTML, Prolog)
  • implicit finalization forms

Various standard Common Lisp features also need to be implemented as macros, such as:

  • the standard setf abstraction, to allow custom compile-time expansions of assignment/access operators
  • with-accessors, with-slots, with-open-file and other similar WITH macros
  • Depending on implementation, if or cond is a macro built on the other, the special operator; when and unless consist of macros
  • The powerful loop domain-specific language

Macros are defined by the defmacro macro. The special operator macrolet allows the definition of local (lexically scoped) macros. It is also possible to define macros for symbols using define-symbol-macro and symbol-macrolet.

Paul Graham's book On Lisp describes the use of macros in Common Lisp in detail. Doug Hoyte's book Let Over Lambda extends the discussion on macros, claiming "Macros are the single greatest advantage that lisp has as a programming language and the single greatest advantage of any programming language." Hoyte provides several examples of iterative development of macros.

Example using a macro to define a new control structure

[edit]

Macros allow Lisp programmers to create new syntactic forms in the language. One typical use is to create new control structures. The example macro provides an until looping construct. The syntax is:

(until test form*)

The macro definition for until:

(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))
    `(tagbody ,start-tag
              (when ,test (go ,end-tag))
              (progn ,@body)
              (go ,start-tag)
              ,end-tag)))

tagbody is a primitive Common Lisp special operator which provides the ability to name tags and use the go form to jump to those tags. The backquote ` provides a notation that provides code templates, where the value of forms preceded with a comma are filled in. Forms preceded with comma and at-sign are spliced in. The tagbody form tests the end condition. If the condition is true, it jumps to the end tag. Otherwise, the provided body code is executed and then it jumps to the start tag.

An example of using the above until macro:

(until (= (random 10) 0)
  (write-line "Hello"))

The code can be expanded using the function macroexpand-1. The expansion for the above example looks like this:

(TAGBODY
 #:START1136
 (WHEN (ZEROP (RANDOM 10))
   (GO #:END1137))
 (PROGN (WRITE-LINE "hello"))
 (GO #:START1136)
 #:END1137)

During macro expansion the value of the variable test is (= (random 10) 0) and the value of the variable body is ((write-line "Hello")). The body is a list of forms.

Symbols are usually automatically upcased. The expansion uses the TAGBODY with two labels. The symbols for these labels are computed by GENSYM and are not interned in any package. Two go forms use these tags to jump to. Since tagbody is a primitive operator in Common Lisp (and not a macro), it will not be expanded into something else. The expanded form uses the when macro, which also will be expanded. Fully expanding a source form is called code walking.

In the fully expanded (walked) form, the when form is replaced by the primitive if:

(TAGBODY
 #:START1136
 (IF (ZEROP (RANDOM 10))
     (PROGN (GO #:END1137))
   NIL)
 (PROGN (WRITE-LINE "hello"))
 (GO #:START1136))
 #:END1137)

All macros must be expanded before the source code containing them can be evaluated or compiled normally. Macros can be considered functions that accept and return S-expressions – similar to abstract syntax trees, but not limited to those. These functions are invoked before the evaluator or compiler to produce the final source code. Macros are written in normal Common Lisp, and may use any Common Lisp (or third-party) operator available.

Variable capture and shadowing

[edit]

Common Lisp macros are capable of what is commonly called variable capture, where symbols in the macro-expansion body coincide with those in the calling context, allowing the programmer to create macros wherein various symbols have special meaning. The term variable capture is somewhat misleading, because all namespaces are vulnerable to unwanted capture, including the operator and function namespace, the tagbody label namespace, catch tag, condition handler and restart namespaces.

Variable capture can introduce software defects. This happens in one of the following two ways:

  • In the first way, a macro expansion can inadvertently make a symbolic reference which the macro writer assumed will resolve in a global namespace, but the code where the macro is expanded happens to provide a local, shadowing definition which steals that reference. Let this be referred to as type 1 capture.
  • The second way, type 2 capture, is just the opposite: some of the arguments of the macro are pieces of code supplied by the macro caller, and those pieces of code are written such that they make references to surrounding bindings. However, the macro inserts these pieces of code into an expansion which defines its own bindings that accidentally captures some of these references.

The Scheme dialect of Lisp provides a macro-writing system which provides the referential transparency that eliminates both types of capture problem. This type of macro system is sometimes called "hygienic", in particular by its proponents (who regard macro systems which do not automatically solve this problem as unhygienic). [citation needed]

In Common Lisp, macro hygiene is ensured one of two different ways.

One approach is to use gensyms: guaranteed-unique symbols which can be used in a macro-expansion without threat of capture. The use of gensyms in a macro definition is a manual chore, but macros can be written which simplify the instantiation and use of gensyms. Gensyms solve type 2 capture easily, but they are not applicable to type 1 capture in the same way, because the macro expansion cannot rename the interfering symbols in the surrounding code which capture its references. Gensyms could be used to provide stable aliases for the global symbols which the macro expansion needs. The macro expansion would use these secret aliases rather than the well-known names, so redefinition of the well-known names would have no ill effect on the macro.

Another approach is to use packages. A macro defined in its own package can simply use internal symbols in that package in its expansion. The use of packages deals with type 1 and type 2 capture.

However, packages don't solve the type 1 capture of references to standard Common Lisp functions and operators. The reason is that the use of packages to solve capture problems revolves around the use of private symbols (symbols in one package, which are not imported into, or otherwise made visible in other packages). Whereas the Common Lisp library symbols are external, and frequently imported into or made visible in user-defined packages.

The following is an example of unwanted capture in the operator namespace, occurring in the expansion of a macro:

 ;; expansion of UNTIL makes liberal use of DO
 (defmacro until (expression &body body)
   `(do () (,expression) ,@body))

 ;; macrolet establishes lexical operator binding for DO
 (macrolet ((do (...) ... something else ...))
   (until (= (random 10) 0) (write-line "Hello")))

The until macro will expand into a form which calls do which is intended to refer to the standard Common Lisp macro do. However, in this context, do may have a completely different meaning, so until may not work properly.

Common Lisp solves the problem of the shadowing of standard operators and functions by forbidding their redefinition. Because it redefines the standard operator do, the preceding is actually a fragment of non-conforming Common Lisp, which allows implementations to diagnose and reject it.

Condition system

[edit]

The condition system is responsible for exception handling in Common Lisp.[18] It provides conditions, handlers and restarts. Conditions are objects describing an exceptional situation (for example an error). If a condition is signaled, the Common Lisp system searches for a handler for this condition type and calls the handler. The handler can now search for restarts and use one of these restarts to automatically repair the current problem, using information such as the condition type and any relevant information provided as part of the condition object, and call the appropriate restart function.

These restarts, if unhandled by code, can be presented to users (as part of a user interface, that of a debugger for example), so that the user can select and invoke one of the available restarts. Since the condition handler is called in the context of the error (without unwinding the stack), full error recovery is possible in many cases, where other exception handling systems would have already terminated the current routine. The debugger itself can also be customized or replaced using the *debugger-hook* dynamic variable. Code found within unwind-protect forms such as finalizers will also be executed as appropriate despite the exception.

In the following example (using Symbolics Genera) the user tries to open a file in a Lisp function test called from the Read-Eval-Print-LOOP (REPL), when the file does not exist. The Lisp system presents four restarts. The user selects the Retry OPEN using a different pathname restart and enters a different pathname (lispm-init.lisp instead of lispm-int.lisp). The user code does not contain any error handling code. The whole error handling and restart code is provided by the Lisp system, which can handle and repair the error without terminating the user code.

Command: (test ">zippy>lispm-int.lisp")

Error: The file was not found.
       For lispm:>zippy>lispm-int.lisp.newest

LMFS:OPEN-LOCAL-LMFS-1
   Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"

s-A, <Resume>: Retry OPEN of lispm:>zippy>lispm-int.lisp.newest
s-B:           Retry OPEN using a different pathname
s-C, <Abort>:  Return to Lisp Top Level in a TELNET server
s-D:           Restart process TELNET terminal

-> Retry OPEN using a different pathname
Use what pathname instead [default lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest

...the program continues

Common Lisp Object System (CLOS)

[edit]

Common Lisp includes a toolkit for object-oriented programming, the Common Lisp Object System or CLOS. Peter Norvig explains how many Design Patterns are simpler to implement in a dynamic language with the features of CLOS (Multiple Inheritance, Mixins, Multimethods, Metaclasses, Method combinations, etc.).[19] Several extensions to Common Lisp for object-oriented programming have been proposed to be included into the ANSI Common Lisp standard, but eventually CLOS was adopted as the standard object-system for Common Lisp. CLOS is a dynamic object system with multiple dispatch and multiple inheritance, and differs radically from the OOP facilities found in static languages such as C++ or Java. As a dynamic object system, CLOS allows changes at runtime to generic functions and classes. Methods can be added and removed, classes can be added and redefined, objects can be updated for class changes and the class of objects can be changed.

CLOS has been integrated into ANSI Common Lisp. Generic functions can be used like normal functions and are a first-class data type. Every CLOS class is integrated into the Common Lisp type system. Many Common Lisp types have a corresponding class. There is more potential use of CLOS for Common Lisp. The specification does not say whether conditions are implemented with CLOS. Pathnames and streams could be implemented with CLOS. These further usage possibilities of CLOS for ANSI Common Lisp are not part of the standard. Actual Common Lisp implementations use CLOS for pathnames, streams, input–output, conditions, the implementation of CLOS itself and more.

Compiler and interpreter

[edit]

A Lisp interpreter directly executes Lisp source code provided as Lisp objects (lists, symbols, numbers, ...) read from s-expressions. A Lisp compiler generates bytecode or machine code from Lisp source code. Common Lisp allows both individual Lisp functions to be compiled in memory and the compilation of whole files to externally stored compiled code (fasl files).

Several implementations of earlier Lisp dialects provided both an interpreter and a compiler. Unfortunately often the semantics were different. These earlier Lisps implemented lexical scoping in the compiler and dynamic scoping in the interpreter. Common Lisp requires that both the interpreter and compiler use lexical scoping by default. The Common Lisp standard describes both the semantics of the interpreter and a compiler. The compiler can be called using the function compile for individual functions and using the function compile-file for files. Common Lisp allows type declarations and provides ways to influence the compiler code generation policy. For the latter various optimization qualities can be given values between 0 (not important) and 3 (most important): speed, space, safety, debug and compilation-speed.

There is also a function to evaluate Lisp code: eval. eval takes code as pre-parsed s-expressions and not, like in some other languages, as text strings. This way code can be constructed with the usual Lisp functions for constructing lists and symbols and then this code can be evaluated with the function eval. Several Common Lisp implementations (like Clozure CL and SBCL) are implementing eval using their compiler. This way code is compiled, even though it is evaluated using the function eval.

The file compiler is invoked using the function compile-file. The generated file with compiled code is called a fasl (from fast load) file. These fasl files and also source code files can be loaded with the function load into a running Common Lisp system. Depending on the implementation, the file compiler generates byte-code (for example for the Java Virtual Machine), C language code (which then is compiled with a C compiler) or, directly, native code.

Common Lisp implementations can be used interactively, even though the code gets fully compiled. The idea of an Interpreted language thus does not apply for interactive Common Lisp.

The language makes a distinction between read-time, compile-time, load-time, and run-time, and allows user code to also make this distinction to perform the wanted type of processing at the wanted step.

Some special operators are provided to especially suit interactive development; for instance, defvar will only assign a value to its provided variable if it wasn't already bound, while defparameter will always perform the assignment. This distinction is useful when interactively evaluating, compiling and loading code in a live image.

Some features are also provided to help writing compilers and interpreters. Symbols consist of first-level objects and are directly manipulable by user code. The progv special operator allows to create lexical bindings programmatically, while packages are also manipulable. The Lisp compiler is available at runtime to compile files or individual functions. These make it easy to use Lisp as an intermediate compiler or interpreter for another language.

Code examples

[edit]

Birthday paradox

[edit]

The following program calculates the smallest number of people in a room for whom the probability of unique birthdays is less than 50% (the birthday paradox, where for 1 person the probability is obviously 100%, for 2 it is 364/365, etc.). The answer is 23.

In Common Lisp, by convention, constants are enclosed with + characters.

(defconstant +year-size+ 365)

(defun birthday-paradox (probability number-of-people)
  (let ((new-probability (* (/ (- +year-size+ number-of-people)
                               +year-size+)
                            probability)))
    (if (< new-probability 0.5)
        (1+ number-of-people)
        (birthday-paradox new-probability (1+ number-of-people)))))

Calling the example function using the REPL (Read Eval Print Loop):

CL-USER > (birthday-paradox 1.0 1)
23

Sorting a list of person objects

[edit]

We define a class person and a method for displaying the name and age of a person. Next we define a group of persons as a list of person objects. Then we iterate over the sorted list.

(defclass person ()
  ((name :initarg :name :accessor person-name)
   (age  :initarg :age  :accessor person-age))
  (:documentation "The class PERSON with slots NAME and AGE."))

(defmethod display ((object person) stream)
  "Displaying a PERSON object to an output stream."
  (with-slots (name age) object
    (format stream "~a (~a)" name age)))

(defparameter *group*
  (list (make-instance 'person :name "Bob"   :age 33)
        (make-instance 'person :name "Chris" :age 16)
        (make-instance 'person :name "Ash"   :age 23))
  "A list of PERSON objects.")

(dolist (person (sort (copy-list *group*)
                      #'>
                      :key #'person-age))
  (display person *standard-output*)
  (terpri))

It prints the three names with descending age.

Bob (33)
Ash (23)
Chris (16)

Exponentiating by squaring

[edit]

Use of the LOOP macro is demonstrated:

(defun power (x n)
  (loop with result = 1
        while (plusp n)
        when (oddp n) do (setf result (* result x))
        do (setf x (* x x)
                 n (truncate n 2))
        finally (return result)))

Example use:

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

Compare with the built in exponentiation:

CL-USER > (= (expt 2 200) (power 2 200))
T

Find the list of available shells

[edit]

WITH-OPEN-FILE is a macro that opens a file and provides a stream. When the form is returning, the file is automatically closed. FUNCALL calls a function object. The LOOP collects all lines that match the predicate.

(defun list-matching-lines (file predicate)
  "Returns a list of lines in file, for which the predicate applied to
 the line returns T."
  (with-open-file (stream file)
    (loop for line = (read-line stream nil nil)
          while line
          when (funcall predicate line)
          collect it)))

The function AVAILABLE-SHELLS calls the above function LIST-MATCHING-LINES with a pathname and an anonymous function as the predicate. The predicate returns the pathname of a shell or NIL (if the string is not the filename of a shell).

(defun available-shells (&optional (file #p"/etc/shells"))
  (list-matching-lines
   file
   (lambda (line)
     (and (plusp (length line))
          (char= (char line 0) #\/)
          (pathname
           (string-right-trim '(#\space #\tab) line))))))

Example results (on Mac OS X 10.6):

CL-USER > (available-shells)
(#P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh")

Comparison with other Lisps

[edit]

Common Lisp is most frequently compared with, and contrasted to, Scheme—if only because they are the two most popular Lisp dialects. Scheme predates CL, and comes not only from the same Lisp tradition but from some of the same engineers—Guy Steele, with whom Gerald Jay Sussman designed Scheme, chaired the standards committee for Common Lisp.

Common Lisp is a general-purpose programming language, in contrast to Lisp variants such as Emacs Lisp and AutoLISP which are extension languages embedded in particular products (GNU Emacs and AutoCAD, respectively). Unlike many earlier Lisps, Common Lisp (like Scheme) uses lexical variable scope by default for both interpreted and compiled code.

Most of the Lisp systems whose designs contributed to Common Lisp—such as ZetaLisp and Franz Lisp—used dynamically scoped variables in their interpreters and lexically scoped variables in their compilers. Scheme introduced the sole use of lexically scoped variables to Lisp; an inspiration from ALGOL 68. CL supports dynamically scoped variables as well, but they must be explicitly declared as "special". There are no differences in scoping between ANSI CL interpreters and compilers.

Common Lisp is sometimes termed a Lisp-2 and Scheme a Lisp-1, referring to CL's use of separate namespaces for functions and variables. (In fact, CL has many namespaces, such as those for go tags, block names, and loop keywords). There is a long-standing controversy between CL and Scheme advocates over the tradeoffs involved in multiple namespaces. In Scheme, it is (broadly) necessary to avoid giving variables names that clash with functions; Scheme functions frequently have arguments named lis, lst, or lyst so as not to conflict with the system function list. However, in CL it is necessary to explicitly refer to the function namespace when passing a function as an argument—which is also a common occurrence, as in the sort example above.

CL also differs from Scheme in its handling of Boolean values. Scheme uses the special values #t and #f to represent truth and falsity. CL follows the older Lisp convention of using the symbols T and NIL, with NIL standing also for the empty list. In CL, any non-NIL value is treated as true by conditionals, such as if, whereas in Scheme all non-#f values are treated as true. These conventions allow some operators in both languages to serve both as predicates (answering a Boolean-valued question) and as returning a useful value for further computation, but in Scheme the value '() which is equivalent to NIL in Common Lisp evaluates to true in a Boolean expression.

Lastly, the Scheme standards documents require tail-call optimization, which the CL standard does not. Most CL implementations do offer tail-call optimization, although often only when the programmer uses an optimization directive. Nonetheless, common CL coding style does not favor the ubiquitous use of recursion that Scheme style prefers—what a Scheme programmer would express with tail recursion, a CL user would usually express with an iterative expression in do, dolist, loop, or (more recently) with the iterate package.

Implementations

[edit]

See the Category Common Lisp implementations.

Common Lisp is defined by a specification (like Ada and C) rather than by one implementation (like Perl). There are many implementations, and the standard details areas in which they may validly differ.

In addition, implementations tend to come with extensions, which provide functionality not covered in the standard:

  • Interactive Top-Level (REPL)
  • Garbage Collection
  • Debugger, Stepper and Inspector
  • Weak data structures (hash tables)
  • Extensible sequences
  • Extensible LOOP
  • Environment access
  • CLOS Meta-object Protocol
  • CLOS based extensible streams
  • CLOS based Condition System
  • Network streams
  • Persistent CLOS
  • Unicode support
  • Foreign-Language Interface (often to C)
  • Operating System interface
  • Java Interface
  • Threads and Multiprocessing
  • Application delivery (applications, dynamic libraries)
  • Saving of images

Free and open-source software libraries have been created to support extensions to Common Lisp in a portable way, and are most notably found in the repositories of the Common-Lisp.net[20] and CLOCC (Common Lisp Open Code Collection)[21] projects.

Common Lisp implementations may use any mix of native code compilation, byte code compilation or interpretation. Common Lisp has been designed to support incremental compilers, file compilers and block compilers. Standard declarations to optimize compilation (such as function inlining or type specialization) are proposed in the language specification. Most Common Lisp implementations compile source code to native machine code. Some implementations can create (optimized) stand-alone applications. Others compile to interpreted bytecode, which is less efficient than native code, but eases binary-code portability. Some compilers compile Common Lisp code to C code. The misconception that Lisp is a purely interpreted language is most likely because Lisp environments provide an interactive prompt and that code is compiled one-by-one, in an incremental way. With Common Lisp incremental compilation is widely used.

Some Unix-based implementations (CLISP, SBCL) can be used as a scripting language; that is, invoked by the system transparently in the way that a Perl or Unix shell interpreter is.[22]

List of implementations

[edit]

Commercial implementations

[edit]
Allegro Common Lisp
for Microsoft Windows, FreeBSD, Linux, Apple macOS and various UNIX variants. Allegro CL provides an Integrated Development Environment (IDE) (for Windows and Linux) and extensive capabilities for application delivery.
Liquid Common Lisp
formerly called Lucid Common Lisp. Only maintenance, no new releases.
LispWorks
for Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android and various UNIX variants. LispWorks provides an Integrated Development Environment (IDE) (available for most platforms, but not for iOS and Android) and extensive capabilities for application delivery.
mocl
for iOS, Android and macOS.
Open Genera
for DEC Alpha.
Scieneer Common Lisp
which is designed for high-performance scientific computing.

Freely redistributable implementations

[edit]
Armed Bear Common Lisp (ABCL)
A CL implementation that runs on the Java Virtual Machine.[23] It includes a compiler to Java byte code, and allows access to Java libraries from CL. It was formerly just a component of the Armed Bear J Editor.
Clasp
A LLVM based implementation that seamlessly interoperates with C++ libraries. Runs on several Unix and Unix-like systems (including macOS).
CLISP
A bytecode-compiling implementation, portable and runs on several Unix and Unix-like systems (including macOS), as well as Microsoft Windows and several other systems.
Clozure CL (CCL)
Originally a free and open-source fork of Macintosh Common Lisp. As that history implies, CCL was written for the Macintosh, but Clozure CL now runs on macOS, FreeBSD, Linux, Solaris and Windows. 32 and 64 bit x86 ports are supported on each platform. Additionally there are Power PC ports for Mac OS and Linux. CCL was previously known as OpenMCL, but that name is no longer used, to avoid confusion with the open source version of Macintosh Common Lisp.
CMUCL
Originally from Carnegie Mellon University, now maintained as free and open-source software by a group of volunteers. CMUCL uses a fast native-code compiler. It is available on Linux and BSD for Intel x86; Linux for Alpha; macOS for Intel x86 and PowerPC; and Solaris, IRIX, and HP-UX on their native platforms.
Corman Common Lisp
for Microsoft Windows. In January 2015 Corman Lisp has been published under MIT license.[24]
Embeddable Common Lisp (ECL)
ECL includes a bytecode interpreter and compiler. It can also compile Lisp code to machine code via a C compiler. ECL then compiles Lisp code to C, compiles the C code with a C compiler and can then load the resulting machine code. It is also possible to embed ECL in C programs, and C code into Common Lisp programs.
GNU Common Lisp (GCL)
The GNU Project's Lisp compiler. Not yet fully ANSI-compliant, GCL is however the implementation of choice for several large projects including the mathematical tools Maxima, AXIOM and (historically) ACL2. GCL runs on Linux under eleven different architectures, and also under Windows, Mac OS, Solaris, and FreeBSD.
Macintosh Common Lisp (MCL)
Version 5.2 for Apple Macintosh computers with a PowerPC processor running Mac OS X is open source. RMCL (based on MCL 5.2) runs on Intel-based Apple Macintosh computers using the Rosetta binary translator from Apple.
ManKai Common Lisp (MKCL)
A branch of ECL. MKCL emphasises reliability, stability and overall code quality through a heavily reworked, natively multi-threaded, runtime system. On Linux, MKCL features a fully POSIX compliant runtime system.
Movitz
Implements a Lisp environment for x86 computers without relying on any underlying OS.
Poplog
Poplog implements a version of CL, with POP-11, and optionally Prolog, and Standard ML (SML), allowing mixed language programming. For all, the implementation language is POP-11, which is compiled incrementally. It also has an integrated Emacs-like editor that communicates with the compiler.
Steel Bank Common Lisp (SBCL)
A branch from CMUCL. "Broadly speaking, SBCL is distinguished from CMU CL by a greater emphasis on maintainability."[25] SBCL runs on the platforms CMUCL does, except HP/UX; in addition, it runs on Linux for AMD64, PowerPC, SPARC, MIPS, Windows x86 and AMD64.[26] SBCL does not use an interpreter by default; all expressions are compiled to native code unless the user switches the interpreter on. The SBCL compiler generates fast native code according to a previous version of The Computer Language Benchmarks Game.[27]
Ufasoft Common Lisp
port of CLISP for windows platform with core written in C++.

Other implementations

[edit]
Austin Kyoto Common Lisp
an evolution of Kyoto Common Lisp by Bill Schelter
Butterfly Common Lisp
an implementation written in Scheme for the BBN Butterfly multi-processor computer[28][29]
CLICC
a Common Lisp to C compiler[30]
CLOE
Common Lisp for PCs by Symbolics
Codemist Common Lisp
used for the commercial version of the computer algebra system Axiom[31][32]
ExperCommon Lisp
an early implementation for the Apple Macintosh by ExperTelligence
Golden Common Lisp
an implementation for the PC by GoldHill Inc.[33][34]
Ibuki Common Lisp
a commercialized version of Kyoto Common Lisp
Kyoto Common Lisp
the first Common Lisp compiler that used C as a target language. GCL, ECL and MKCL originate from this Common Lisp implementation.
L
a small version of Common Lisp for embedded systems developed by IS Robotics, now iRobot[35]
Lisp Machines (from Symbolics, TI[36][37] and Xerox[38])
provided implementations of Common Lisp in addition to their native Lisp dialect (Lisp Machine Lisp or Interlisp). CLOS was also available. Symbolics provides an enhanced version Common Lisp.[39][40][41]
Procyon Common Lisp
an implementation for Windows and Mac OS, used by Franz for their Windows port of Allegro CL
Star Sapphire Common LISP
an implementation for the PC
SubL
a variant of Common Lisp used for the implementation of the Cyc knowledge-based system[42]
Top Level Common Lisp
an early implementation for concurrent execution[43]
WCL
a shared library implementation[44][45]
VAX Common Lisp
Digital Equipment Corporation's implementation that ran on VAX systems running VMS or ULTRIX
XLISP
an implementation written by David Betz[46]

Applications

[edit]

Common Lisp is used to develop research applications (often in Artificial Intelligence), for rapid development of prototypes or for deployed applications.

Common Lisp is used in many commercial applications, including the Yahoo! Store web-commerce site, which originally involved Paul Graham and was later rewritten in C++ and Perl.[47] Other notable examples include:

There also exist open-source applications written in Common Lisp, such as:

See also

[edit]

References

[edit]

Bibliography

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Common Lisp is a general-purpose, multi-paradigm programming language that serves as a standardized dialect of the Lisp family, supporting functional, procedural, and object-oriented programming paradigms through features like first-class functions, a powerful macro system for metaprogramming, dynamic typing, and automatic garbage collection for memory management. It emphasizes extensibility, allowing programmers to adapt the language to domain-specific needs, and includes the Common Lisp Object System (CLOS) for object-oriented capabilities such as multiple inheritance and generic functions. The language was designed to promote portability across implementations, with its core specification defining syntax, evaluation rules, data types, and over 900 functions and macros in the COMMON-LISP package. Developed in the early as a successor to diverse Lisp dialects like MacLisp, Lisp Machine Lisp, NIL, and others, Common aimed to unify fragmented implementations prevalent in and symbolic computation research. efforts began with the formation of the X3J13 technical committee in under the (ANSI), culminating in the publication of the official standard, ANSI INCITS 226-1994 (formerly X3.226-1994), on December 7, 1994. This ~1,100-page document ensures conformance across systems by specifying required behaviors, optional extensions, and implementation-dependent details, while excluding non-portable features from earlier dialects. Key aspects of Common Lisp include its condition system for robust error handling and restarts, support for and complex numbers, and facilities for packages and symbols to manage namespaces effectively. The language's interactive development environment, often enhanced by tools like SLIME for integration, facilitates rapid prototyping and debugging, making it suitable for applications in AI, web development, and . Despite competition from more modern languages, Common Lisp remains influential due to its maturity, with active implementations like SBCL, CLISP, and commercial ones from LispWorks and Franz Inc. supporting contemporary systems.

History

Origins and Early Development

Common Lisp originated in the early 1980s as an effort to unify the fragmented landscape of Lisp dialects that had evolved primarily for research. By the late 1970s, variants such as Maclisp—developed at MIT for the and to support system-building tools and numerical computations like arbitrary-precision integers—ZetaLisp (an extension of Maclisp for Lisp Machines with a 24-bit ), and NIL (an extended Maclisp for VAX and S-1 systems incorporating message-passing objects) had diverged significantly, complicating portability and collaboration across AI projects. This fragmentation prompted the Lisp community to seek a common that could serve as a stable foundation for implementations while preserving the strengths of these predecessors, including influences from Spice Lisp and Scheme. Key figures Guy L. Steele Jr. and Richard P. Gabriel played pivotal roles in initiating the unification process. In April 1981, at an ARPA-convened Lisp Community Meeting, Steele, Gabriel, and Jon L. White proposed creating a common Lisp standard to consolidate the Maclisp-descended dialects, leading to the formation of the Common Lisp Group in June 1981. This group, involving contributors like Scott Fahlman and David A. Moon, conducted meetings and email-based discussions to design a shared language. Their efforts culminated in the 1984 publication of Common Lisp: The Language by Steele, which outlined the core features of the proposed dialect, including lexical scoping, dynamic variables, and condition handling, as a descriptive specification rather than a prescriptive standard. Early implementations emerged in the mid-1980s to test and refine the design. Portable Standard Lisp (PSL), evolved from Standard Lisp by Anthony Hearn and Martin Griss, emphasized portability across over a dozen platforms with a retargetable and features like SYSLISP for system-level programming, making it a key tool for validating Common Lisp compatibility on stock hardware. Similarly, Common Lisp (KCL), released in 1984 by Taiichi Yuasa and Masami Hagiya and based on an early draft, was written for systems, exposing specification issues and influencing extensions like parallel constructs developed at Stanford by Gabriel and John McCarthy. These implementations accelerated adoption by demonstrating feasibility and portability. In response to growing interest, the (ANSI) formed the X3J13 technical committee in 1986, with its first meeting in September of that year, to formalize Common Lisp as a national standard.

Standardization and Evolution

The X3J13 committee, chartered by the American National Standards Institute (ANSI) in 1986 and holding its first meeting in September 1986, was responsible for developing a formal standard for Common Lisp through extensive deliberation until its final meetings in 1994. This subcommittee of ANSI's X3 technical committee addressed over 1,000 technical issues to reconcile divergences among existing Lisp dialects, ensuring portability and consistency across implementations. Key resolutions included the adoption of an extended LOOP macro for iteration, which provided a domain-specific language with pseudo-English syntax for complex control structures, and the specification of a standardized pretty printer interface to handle formatted output of Lisp expressions. The committee's efforts culminated in the publication of ANSI X3.226-1994 on December 8, 1994, a comprehensive specification exceeding 1,000 pages that defined the core features of the language, including data types, evaluation semantics, and the (CLOS). As the project editor from 1990 to 1994, Kent Pitman oversaw the document's finalization, which was later reaffirmed as INCITS 226-1994 (R1999) and (R2004). This standard remains the definitive reference for Common Lisp implementations, promoting interoperability without subsequent revisions through 2025. Following the ANSI standard's release, community efforts focused on accessible references and extensions. In 1996, Kent Pitman prepared the Common Lisp HyperSpec, an online hypertext version of the standard with approximately 105,000 hyperlinks across 2,300 files, revised in 2005 to incorporate clarifications. Proposed updates, such as iterative method combination for CLOS, emerged in post-ANSI discussions to enhance dispatch by allowing sequential application of methods in a loop-like manner, though these have not been incorporated into a new standard. Community-driven evolution has been advanced through conferences like the European Lisp Symposium (ELS), held annually since 2008, and the International Lisp Conference (ILC), with proceedings documenting innovations in Common Lisp applications and implementations. For instance, ELS papers have explored extensions to the Metaobject Protocol (MOP) underlying CLOS, while ILC has featured advancements in build systems like ASDF. Despite the absence of a new ANSI standard by 2025, the ecosystem has grown significantly, exemplified by Quicklisp, a library manager released in beta in 2010 by Zach Beane, which facilitates downloading and loading over 1,500 community libraries, boosting adoption in domains like and symbolic computation.

Syntax

S-Expressions and Notation

S-expressions, or symbolic expressions, form the foundational syntax of Common Lisp, representing both data and code structures in a uniform manner. An S-expression is defined as either an atom or a list; atoms include symbols (such as foo or nil), numbers (like 42 or 3.14), strings (delimited by double quotes, e.g., "hello"), and characters (prefixed with #\, e.g., #\a), while lists are finite sequences of S-expressions enclosed in parentheses, such as (a b c). This recursive structure enables lists to contain other lists or atoms, with internal representation relying on cons cells where each cell has a car (first element) and cdr (rest), terminating in nil for proper lists. Common Lisp employs prefix notation in S-expressions, placing the operator or function name first, followed by its arguments, as in (+ 1 2) which denotes of 1 and 2. Nesting allows complex expressions, such as (+ (* 2 3) 4), facilitating hierarchical representations. This design promotes , where code is treated as data—S-expressions can be quoted with ' to yield lists manipulable like any other , enabling capabilities central to . Literal notations provide direct representation: numbers appear as integers (42), floats (3.14), or ratios (1/2); strings use double quotes ("text"); and characters employ #\ (#\newline). Whitespace, including spaces and newlines, is insignificant except to separate tokens, allowing flexible formatting for readability. Comments in Common Lisp S-expressions use a (;) to initiate line comments, ignoring all characters from the semicolon to the end of the line; multiple leading semicolons (e.g., ;; or ;;;) conventionally indicate indentation levels for alignment. Block comments are supported via the reader macro #| ... |#, which discards enclosed content across multiple lines.

Reader and Evaluation Semantics

The Common Lisp reader is responsible for parsing textual input into Lisp objects, primarily S-expressions, by tokenizing characters from an input stream according to the rules defined in the readtable. The process begins by reading individual characters and classifying them as whitespace (discarded and skipped), macro characters (which invoke associated reader macro functions for special parsing), escape characters (to handle literal interpretation), or constituent characters (which accumulate into tokens representing symbols, numbers, or other objects). Dispatching macro characters, such as #, introduce specialized syntax by calling a reader macro function that processes subsequent characters to construct complex objects like vectors or bit-strings; for example, the dispatch macro #\ (backslash) handles character literals, which may include escapes or named characters like #\newline, while #* reads bit-strings. Invalid characters signal a reader-error, ensuring well-formed input. In interactive environments, the evaluation model operates through the read-eval-print loop (REPL), which repeatedly reads a form from standard input using the read function, evaluates it in the current environment, and prints the resulting value(s) to standard output. The REPL maintains special variables like *, **, and *** to track previous results for convenient access, and it catches errors to prevent termination, allowing continued interaction. Evaluation itself follows a recursive model where a form is processed based on its type: self-evaluating objects, such as numbers, characters, or strings, return themselves unchanged; symbols are looked up in the lexical and dynamic environments to yield their bound values; and lists are interpreted as function applications, where the first element (the operator) is evaluated to a function, and the remaining elements (arguments) are evaluated from left to right before being passed to the function. For instance, evaluating (+ 1 2) first evaluates + to the addition function, then 1 and 2 as self-evaluating numbers, and applies the function to return 3. The quote special form provides a mechanism to prevent , treating its argument as literal rather than executable code. Written as (quote form) or abbreviated with a single quote prefix ', it returns the form unevaluated; for example, '(+ 1 2) yields the (+ 1 2) as a , not the result 3. This is essential for representing programs as , such as in macros, where symbols and s must be manipulated without their subforms being executed prematurely. Self-evaluating objects do not require quoting, as their evaluation yields themselves, but quoting ensures symbols (which would otherwise bind to variables) and conses (which would apply as functions) are treated as constants. Common Lisp distinguishes multiple evaluation phases to support and separate concerns like code generation from execution: read time (when parses input into objects), compile time (when compile-file processes forms for code generation), load time (when a compiled file is loaded into the environment), and run time (during actual program execution or interpretation). The eval-when special form controls which phases execute a body of forms, using situations like :compile-toplevel (for top-level compile-time effects), :load-toplevel (for loading compiled files), and :execute (for run-time or interpreted evaluation). For example, (eval-when (:compile-toplevel :load-toplevel :execute) (some-form)) ensures some-form runs in all phases, enabling actions like defining reader macros during compilation or loading. Non-top-level uses of eval-when only affect the :execute situation, returning nil otherwise, to avoid unintended side effects.

Data Types

Primitive Scalar Types

Common Lisp's primitive scalar types encompass the fundamental atomic data types that represent single, indivisible values, serving as the building blocks for more complex structures. These include numeric types for mathematical computations, characters for textual representation, symbols for naming entities, and booleans for logical values. Unlike composite types, scalar types cannot be decomposed into smaller parts within the language's core semantics. Type specifiers allow precise description of these types, enabling compiler optimizations through declarations.

Numeric Types

Numeric types in Common Lisp form a hierarchy under the supertype number, with primitive scalars including integers, ratios, and floats. Integers represent exact mathematical integers without bounds on magnitude, partitioned exhaustively into fixnum and bignum subtypes. A fixnum is an implementation-defined integer within the range from most-negative-fixnum to most-positive-fixnum, typically corresponding to machine-word-sized integers for efficient arithmetic; for example, in many implementations, this spans approximately -2^{30} to 2^{30}-1. Outside this range, integers are bignums, which support arbitrary precision through multi-word representations, ensuring exact computation for large values without overflow errors. Ratios, a subtype of rational, represent exact fractional values as reduced fractions of two integers (numerator and denominator), such as 1/3 for exact division. They are created automatically during operations like / on integers when the result is not , and support exact arithmetic without . The complex type, a supertype parallel to real, represents complex numbers as a pair of real numbers (realpart and imaginarypart), denoted in source code as #c(real imag), such as #c(1 2) for 1 + 2i. Operations on complex numbers extend those on reals, with automatic promotion as needed. Floating-point types, subtypes of float, approximate real numbers using a sign, significand, base, exponent, and precision model, often aligned with IEEE 754 standards in practice. The subtypes short-float, single-float, double-float, and long-float provide varying precision and exponent ranges, with minimum requirements of 13/5 bits, 24/8 bits, 50/8 bits, and 50/8 bits respectively; any two subtypes must be either disjoint or identical, and implementations may equate some (e.g., double-float and long-float). For instance, single-float is commonly used for general computations due to its balance of speed and precision, while double-float offers higher accuracy for scientific applications.

Character Type

The character type represents a unitary token in text, abstracting away encoding details to support portable string handling. Characters are denoted in source code using the #\ reader syntax followed by a name or code point, such as #\A for the Latin capital A or #\λ for the Greek lambda, with modern implementations providing full Unicode support via code points up to U+10FFFF. The type partitions exhaustively into base-char (upgraded from standard-char, covering 96 basic characters like ASCII) and extended-char for additional glyphs. This design allows characters to be compared, hashed, and used in predicates like alpha-char-p.

Symbol Type

Symbols are unique, interned objects serving as for variables, functions, and other named entities, stored in packages to manage namespaces and avoid name clashes. Each has a print name (a ), a for metadata, and can be bound to values or function definitions; for example, a is "bound" if it holds a variable value via symbol-value and "fbound" if it names a function via fboundp. Symbols are created via interning strings in packages (e.g., the COMMON-LISP-USER package), ensuring global uniqueness within their package. Keywords, a subtype, are self-quoting symbols in the KEYWORD package, often used for optional arguments.

Boolean Type

The boolean type consists solely of two symbols: t (true) and nil (false), with nil also denoting the empty list and false in logical contexts. In conditionals like if, any non-nil value acts as a generalized boolean true, but t is the canonical true value for clarity. This design integrates booleans seamlessly with symbols, avoiding separate primitives while supporting logical operations.

Type Specifiers

Type specifiers describe sets of objects, using atomic symbols (e.g., integer, float) or compound lists for refined subsets, such as (integer 0 100) for integers in that closed interval or (float (0.0) (1.0)) for unit-range floats. These specifiers enable declarations like (declare (type (integer 0 100) x)) to inform the compiler of value constraints, potentially optimizing code generation by avoiding type checks or enabling specialized arithmetic. Specifiers can include wildcards like * for unbounded dimensions (e.g., (integer * *) equivalents integer), and new ones are defined via deftype for user extensions. Functions like typep test membership, while subtypep checks relationships.

Composite Data Structures

Common Lisp provides several built-in composite data structures for aggregating multiple values, enabling the construction of complex data representations from primitive types such as numbers and symbols. These structures include cons cells as the foundational linked pairs, lists built upon them, multidimensional arrays for indexed access, hash tables for associative storage, and user-defined structures for record-like organization. Each type supports operations tailored to its purpose, facilitating efficient manipulation while integrating seamlessly with the language's . The cell serves as the basic unit for composite structures, consisting of a pair with two slots: the (contents of the address register) and the cdr (contents of the decrement register), historically derived from hardware terminology. A cell is created using the cons function, which takes two arguments of any type and returns a new object representing the dotted pair ([car](/page/Car) . cdr). For instance, ([cons](/page/Cons) 1 2) yields a cell where the holds 1 and the cdr holds 2, accessible via the car and cdr functions, respectively. cells form the basis for more complex aggregates like and trees, with the type specifier cons denoting objects that are neither atoms nor the empty list nil. Lists in Common Lisp are chains of cons cells, providing a flexible, sequential collection type. A proper list is a nil-terminated chain where each cons's car holds an element and its cdr points to the next cons or nil, as in (1 2 3) equivalent to (cons 1 (cons 2 (cons 3 nil))). Dotted lists, by contrast, terminate with a non-nil atom, such as (1 2 . 3), and are useful for heterogeneous pairs but less common for homogeneous sequences. Key operations include car and cdr for access, cons for construction, and append for concatenation, which creates a new list by linking the tail of the first to the second: (append '(1 2) '(3)) returns (1 2 3). The type list encompasses proper, dotted, and circular lists, with null partitioning it from cons. Arrays offer contiguous, indexed storage for fixed or dynamic collections, with the number of dimensions (rank) being implementation-defined but typically supporting a large number (far exceeding seven) of dimensions. They are created with make-array, specifying dimensions as a list or and an optional :element-type for specialization, such as (make-array 10 :element-type 'fixnum) for an vector. Vectors are one-dimensional arrays, a subtype allowing efficient linear access via aref or svref for simple vectors. Adjustable arrays, flagged with :adjustable t, permit resizing via adjust-array, while specialized arrays optimize storage and operations for types like base-char or single-float, potentially restricting elements to subtypes of the specified type. Displaced arrays another array's storage without , enhancing for views. Hash tables provide unordered, associative mapping from keys to values, ideal for fast lookups in dynamic datasets. They are instantiated with make-hash-table, optionally specifying a :test function for key , such as :eql (default, using eql for equality), :eq, :equal, or :equalp for case-insensitive or structure-aware matching. For example, (make-hash-table :test 'equal) allows keys, with insertion via (setf (gethash key table) value) and retrieval using gethash. Hash tables support with maphash and size queries via hash-table-[size](/page/Size), maintaining logarithmic average access time regardless of population. Keys and values can be any objects, with rehashing handled automatically upon growth. Structures enable the definition of named, record-like types with fixed slots, offering compile-time efficiency over lists or classes. The defstruct macro declares a structure, specifying slot names and options like initial values or types: (defstruct person (name nil) (age 0 :type [integer](/page/Integer))) creates a type person with reader functions person-name and person-age, a constructor make-person, and a predicate person-p. Instances are built with make-person :name "Alice" :age 30, and slots support setf for mutable access unless marked :read-only. Structures include options for printing, inheritance, and constructors, generating efficient, inline-capable accessors. The type specifier for the structure, such as person, denotes its instances.

Functions and Closures as Types

In Common Lisp, functions are treated as first-class objects within the , allowing them to be manipulated like any other . The function type specifier denotes objects that represent code, which can be stored in variables, passed as arguments to other functions, returned as values, or otherwise operated upon dynamically. This design enables powerful paradigms, where functions are not merely named procedures but full-fledged values that can be created and composed at runtime. The compound type specifier for functions takes the form (function [arg-typespec [value-typespec]]), which describes the expected argument types and return types for declaration purposes, though it is primarily used for optimization hints rather than runtime discrimination. Function objects are typically created using the lambda special form for anonymous functions or the defun macro for named ones, both of which yield instances of the function class. To obtain a function object from a symbol naming a function, the function special operator is used, often abbreviated with the sharp-quote reader macro #', as in #'car which coerces the symbol car to its corresponding function object. These objects can then be invoked explicitly using funcall for a fixed list of arguments or apply for a list of arguments, providing a uniform way to call any function value regardless of its origin. For instance, (funcall #'+ 1 2) evaluates to 3, demonstrating how a built-in function can be treated as data. A key aspect of functions in Common Lisp is the support for closures, which are function objects that capture and retain access to the lexical environment in which they are defined. When a lambda expression is evaluated within a lexical binding form such as let, the resulting function closes over the surrounding variables, preserving their values or bindings beyond the scope of the binding form. This is illustrated by the expression (let ((x 1)) ([lambda](/page/Lambda) () x)), which produces a closure that returns the value 1 when called, even after the let binding has expired. Closures enable the implementation of techniques like callbacks and stateful functionals without global variables. Common Lisp's type system facilitates higher-order functions, where functions accept other functions as arguments or return them as results, exemplified by built-ins like mapcar and reduce. The mapcar function applies its function argument element-wise to lists, such as (mapcar #'1+ '(1 2 3)) yielding (2 3 4), while reduce combines list elements using a binary function, as in (reduce #'+ '(1 2 3)) returning 6. Function signatures are defined via lambda lists, which support advanced parameter handling with keywords like &optional for optional arguments, &rest for variable arguments, and &key for keyword-based arguments, allowing flexible and expressive interfaces. For example, a lambda list (a &optional (b 0) &rest rest &key c) accommodates varying argument counts and named options.

Scope and Environments

Lexical Scope Mechanics

In Common Lisp, lexical scope determines how names are resolved at based on the textual structure of the code, creating nested environments where inner bindings can shadow outer ones without affecting the outer scope. This static resolution ensures that references to variables are bound to the nearest enclosing lexical contour, promoting predictable behavior and enabling optimizations during compilation. The primary mechanism for introducing lexical bindings is through the let and let* special operators, which establish new variable bindings within a specified body of forms. In a let form, all initialization forms are evaluated in parallel before any bindings are established, meaning that the initial values of variables do not depend on each other or on the new bindings; unbound variables default to nil. For example:

(let ((x 1) (y (+ x 2))) ; x here refers to an outer binding, if any (list x y))

(let ((x 1) (y (+ x 2))) ; x here refers to an outer binding, if any (list x y))

This parallel evaluation prevents sequential dependencies within the binding list. In contrast, let* performs bindings sequentially, allowing later initialization forms to reference previously bound variables in the same let* form, which facilitates more expressive code for dependent computations. For instance:

(let* ((x 1) (y (+ x 2))) ; y uses the new x binding (list x y))

(let* ((x 1) (y (+ x 2))) ; y uses the new x binding (list x y))

Both forms create lexical scopes that extend to the body forms, where references to the bound variables resolve to these new bindings, shadowing any outer lexical or dynamic bindings with the same name. The scope of these bindings is strictly lexical, confined to the body and any nested lexical environments, and does not extend to the initialization forms of let itself, though let* includes subsequent initializations in the scope. Free variables in lexical contexts—those not locally bound—are resolved by searching the enclosing lexical environments at of definition, a that underpins the creation of closures. Lambda expressions, which define anonymous functions, inherit the lexical environment of their defining , capturing free variables from outer scopes to form closures that maintain access to those values even after the outer bindings' extent ends. This resolution occurs statically during compilation, ensuring that the closure refers to the specific bindings present at creation, not at invocation. For example, a defined within a let can reference the let-bound variable, preserving its value across calls. Declarations via and proclaim provide compile-time hints that influence lexical optimization, particularly for type information on variables. The declare form, placed within the body of binding constructs like let, specifies properties such as types for bound variables, allowing the compiler to generate more efficient code by avoiding runtime type checks or enabling inline expansions; for instance, declaring a variable as integer permits integer-specific optimizations within its lexical scope. Proclaim, a function that globally asserts declarations, affects subsequent lexical bindings by propagating type or optimization hints across compilation units, though it does not alter the scoping rules themselves. These mechanisms are advisory, and their effects on optimization qualities like speed or safety are implementation-dependent but crucial for performance in lexically scoped code. Lexical exit points and non-local jumps within scopes are managed by block and tagbody. The block special operator names a lexical contour that can be exited non-locally via return-from, providing a structured way to escape from nested computations while respecting lexical boundaries; inner blocks with the same name shadow outer ones, resolving to the nearest enclosing block. Similarly, tagbody introduces lexical tags for unstructured , where go transfers execution to a matching tag (compared via eql) within the same or enclosing tagbody, enabling loops or jumps confined to lexical scope without dynamic side effects. These constructs ensure that control transfers remain predictable and contained within the static structure of the program.

Dynamic Scope and Special Variables

In Common Lisp, special variables provide dynamic scoping, allowing bindings to be visible across function call boundaries during runtime execution. These variables are explicitly declared using the macros defvar or defparameter, which establish a as special and optionally initialize it. The defparameter macro unconditionally assigns the provided initial value to the variable, evaluating the form each time it is executed, whereas defvar assigns the initial value only if the variable is currently unbound, preserving any existing binding otherwise. Both macros proclaim the variable as special, meaning subsequent references to the symbol will resolve to dynamic bindings rather than lexical ones, and it is conventional to name such variables with surrounding asterisks (e.g., *foo*) to indicate their dynamic nature. Dynamic bindings of special variables have a dynamic extent, remaining active from the point of establishment within a binding form—such as let, let*, or progv—through the evaluation of the bound body and any functions called from it, until the binding form exits, unless shadowed by a nested binding. This extent allows callees to access and modify the current binding without explicit parameter passing, facilitating runtime configurability but potentially leading to unexpected interactions in complex call stacks. Special variables are accessed via the symbol's value cell, often directly by referencing the symbol or using the symbol-value function for explicit retrieval. For instance, the standard variable *print-circle* is a global dynamic variable that controls whether the printer detects and handles circular structures during output; when bound to a true value within a let form, it enables the use of #n= and #n# notation to represent shared or circular references, preventing infinite in printing. To debug execution paths involving dynamic bindings, Common Lisp provides the trace macro, which intercepts calls to specified functions and reports entry and exit points with argument and return values, revealing how dynamic variables propagate through the call stack. Complementing this, the step macro enables single-stepping through form evaluation, pausing at each subform to inspect bindings and state, which is particularly useful for tracing the effects of shadowing or modifications to special variables during runtime. Although dynamic scoping offers flexibility for global-like state management, modern Common Lisp practice favors lexical scoping for most variables due to its predictability and avoidance of unintended interactions from distant call sites, reserving special variables primarily for configuration or thread-local parameters.

Functions and Control Structures

Defining and Applying Functions

In Common Lisp, functions are defined using the defun macro, which creates a named and associates it with a in the function , making it accessible via fboundp. The syntax is (defun name lambda-list [[doc-string]] form*), where name is the naming the function, lambda-list specifies the parameter list, and form* are the body forms evaluated in order when the function is called. This definition establishes the function in the global function environment, allowing it to be invoked by name or referenced as a functional object via (function name). Lambda lists in defun support required, optional, and auxiliary parameters, with qualifiers like &optional, &rest, &key, and &aux. The &aux qualifier declares local variables initialized within the function body without appearing as arguments, useful for temporary computations not passed from the caller. For instance:

lisp

(defun compute-sum (numbers &aux total) (setq total 0) (dolist (num numbers) (incf total num)) total)

(defun compute-sum (numbers &aux total) (setq total 0) (dolist (num numbers) (incf total num)) total)

This example defines a function that sums a list of numbers using an auxiliary variable total, which remains local to the function's lexical scope. Functions are typically applied by direct invocation, such as (compute-sum '(1 2 3)), which evaluates to 6. For dynamic invocation, Common Lisp provides funcall and apply. The funcall function applies a functional object to a fixed set of arguments: (funcall function arg*). For example, (funcall #'list 1 2 3) returns (1 2 3). The apply function extends this to variable arguments by accepting a list for the final argument sequence: (apply function arg* arg-list). Thus, (apply #'+ '(1 2 3)) also returns 6, enabling flexible calls where arguments may be constructed at runtime. Both treat the function argument as a designator, coercing symbols to their bound function objects. Performance optimization in function definitions can be achieved through inline declarations, specified via (declare (inline name*)) or globally with (proclaim '(inline name*)). When a function is declared inline, the may expand its body directly at call sites, reducing overhead from function calls, particularly for small, frequently used functions. This declaration affects compilation but not interpretation, and it applies only to named functions defined with defun. Function objects in Common Lisp support equality predicates for comparison. The eq predicate tests for object identity, returning true if two function objects are the same instance, as functions are first-class objects. For structural equality, equalp can be used on function objects, though it typically reduces to identity checks since functions lack defined internal structure for recursive comparison; thus, equalp on distinct function objects usually returns nil unless they are identical. These predicates aid in and optimization by verifying properties.

Lambda Expressions and Closures

In Common Lisp, lambda expressions provide a way to define anonymous functions using the syntax (lambda lambda-list [[declaration* | documentation]] form*), which evaluates to a function object equivalent to (function (lambda ...)). These expressions can be used inline within forms such as funcall or apply, or stored in variables for later invocation, allowing flexible creation of functions without naming them via defun. For instance, the expression #'(lambda (x) (+ x 3)) defines a function that adds 3 to its argument, and applying it as (funcall #'(lambda (x) (+ x 3)) 4) yields 7. Lambda expressions form closures when evaluated within a lexical environment, capturing the surrounding bindings such that the resulting function can refer to and modify those variables even after the binding form has completed. This closure mechanism arises from the function special form, which records the lexical environment active at the time of evaluation, enabling the function to access and alter the captured bindings across multiple invocations. Unlike simple functions, closures maintain shared state among instances that reference the same binding; for example, modifications via setq in one closure affect all others sharing that binding. A classic illustration of closures is a counter function that maintains internal state:

lisp

(let ((count 0)) #'(lambda () (incf count)))

(let ((count 0)) #'(lambda () (incf count)))

Storing this in a variable like *counter* allows repeated calls to (funcall *counter*) to return successive integers (1, 2, 3, etc.), as the closure captures and updates the count binding from the let form. Similarly, closures enable serial number generation by encapsulating a counter for producing unique identifiers without global variables; for instance:

lisp

(defun make-serial-generator (start) (let ((serial start)) #'(lambda () (prog1 serial (incf serial)))))

(defun make-serial-generator (start) (let ((serial start)) #'(lambda () (prog1 serial (incf serial)))))

Invoking (funcall (make-serial-generator 100)) yields 100 on the first call, 101 on the second, and so on, with the closure preserving the mutable serial binding across calls. In contrast to named functions defined with defun, which are symbols in a package's function namespace and can be qualified accordingly, anonymous lambda expressions lack a home package since they are lists rather than symbols, existing solely as function objects without symbolic naming or interning requirements. This distinction makes lambdas ideal for temporary or dynamically generated functions but requires explicit handling, such as via funcall, for application.

Built-in Control Flow Operators

Common Lisp provides a suite of built-in special operators and macros for managing , enabling conditional execution, , and structured exits without relying on low-level jumps. These operators are defined in the ANSI Common Lisp standard and form the foundation for programmatic decision-making and repetition in Lisp code. They emphasize declarative style, where the intent of the control structure is expressed directly in the code, often with lexical bindings and side-effect-free evaluation where possible.

Conditionals

The if special operator implements basic conditional branching by evaluating a test form and selecting between two alternative forms based on its . Its syntax is (if test-form then-form [else-form]), where the test-form is evaluated first; if non-nil, then-form is evaluated and returned, otherwise else-form (if provided) is used. Notably, only one of the then- or else-forms is evaluated, preventing unnecessary . For example:

lisp

(if (> x 0) (print "positive") (print "non-positive"))

(if (> x 0) (print "positive") (print "non-positive"))

This operator is fundamental for simple binary decisions and is specified in the Common Lisp HyperSpec. The when and unless macros extend if for single-branch conditionals without an else clause. when has the form (when test-form &body body), evaluating the body forms only if the test-form yields non-nil, and returning the last body's value or nil. Conversely, unless executes the body if the test is nil. These are useful for imperative-style code with side effects, such as logging or mutations, and implicitly return nil when the condition fails. An example of when is:

lisp

(when (listp item) (push item collection))

(when (listp item) (push item collection))

Their definitions ensure sequential evaluation of body forms. For multi-way branching, the cond special operator allows selection among multiple clauses, each consisting of a test form followed by zero or more consequent forms. The syntax is (cond &rest clauses), where clauses are processed sequentially: the first clause whose test-form evaluates to non-nil has its consequents evaluated in order, returning the last one's value; if no clause succeeds, nil is returned. A t test (true) serves as a default case. This operator supports short-circuiting, as evaluation stops at the first true clause. For instance:

lisp

(cond ((> x 0) "positive") ((< x 0) "negative") (t "zero"))

(cond ((> x 0) "positive") ((< x 0) "negative") (t "zero"))

cond is particularly powerful for pattern matching-like logic and is a core part of the language's control primitives.

Looping Constructs

Common Lisp offers several iteration operators tailored to different needs, from general-purpose binding and stepping to list and integer traversal. The do special operator provides a general iteration framework with the syntax (do ((var init step) ...) (end-test &rest result-forms) &body body). It initializes variables with init forms, executes the body repeatedly while the end-test is nil, updates variables via step forms after each iteration, and finally evaluates result-forms upon exit. Variables are lexically scoped to the loop. This construct is ideal for complex loops involving multiple accumulators, as in:

lisp

(do ((i 0 (1+ i)) (sum 0 (+ sum i))) ((> i 10) sum))

(do ((i 0 (1+ i)) (sum 0 (+ sum i))) ((> i 10) sum))

It returns the value of the result-forms or nil if none. For iteration, dolist simplifies traversal with (dolist (var listform &optional result-form) &body body), binding var successively to each element of the list produced by listform, executing body for each, and returning the optional result-form (or nil). It handles the end of the list gracefully without explicit indexing. Example:

lisp

(let ((total 0)) (dolist (num numbers) (incf total num)) total)

(let ((total 0)) (dolist (num numbers) (incf total num)) total)

This macro expands into loop internally, promoting readability for sequential processing. Similarly, dotimes iterates a fixed number of times: (dotimes (var countform &optional result-form) &body body), where var is bound from 0 to one less than the value of countform. It is suited for count-based repetition, returning the result-form or nil. For example:

lisp

(dotimes (i 5) (print i))

(dotimes (i 5) (print i))

Both dolist and dotimes are macros that lexicalize their variables and ensure cleanup on normal exit. The loop macro offers an extensible, for complex iterations, supporting accumulation, nesting, and conditional exits in a keyword-driven syntax. Forms like (loop for i from 1 to 10 sum i) compute sums efficiently, while features like collect, when, and if allow data gathering and filtering. It returns one or more values depending on the clauses and can simulate much of imperative programming's . The full specification includes dozens of keywords for flexibility, making it a for non-trivial loops despite its verbosity.

Block Exits and Protection

To enable early returns from nested control structures, return and return-from provide non-local exits. The block special form delimits a named exit point: (block name &body body), where body executes until a return-from targeting that name is invoked. return-from has syntax (return-from name &optional value), immediately exiting the block and returning value (or nil). Implicit blocks exist around constructs like do, allowing return (equivalent to (return-from nil ...)) for simple escapes. This mechanism supports structured programming without goto-like jumps, as in:

lisp

(block outer (do ((i 0 (1+ i))) ((> i 10)) (when (= i 5) (return-from outer "found"))))

(block outer (do ((i 0 (1+ i))) ((> i 10)) (when (= i 5) (return-from outer "found"))))

These operators ensure the exit value propagates correctly through lexical scopes. For guaranteeing cleanup during exits (normal or abnormal), unwind-protect wraps protected code with cleanup forms: (unwind-protect protected-form &body cleanup-forms). The protected-form executes first; regardless of how it exits (via return, condition, or throw), the cleanup-forms are evaluated sequentially afterward. This is essential for , such as closing files or restoring state. Example usage:

lisp

(unwind-protect (with-open-file (stream file :direction :output) (format stream "data")) (print "cleanup done"))

(unwind-protect (with-open-file (stream file :direction :output) (format stream "data")) (print "cleanup done"))

Cleanup occurs even if protected-form signals an , but the is re-raised after.

Logical Operators

The [and, or](/page/And/or), and not special forms handle boolean logic with for efficiency. and takes forms (and &rest forms) and evaluates them left-to-right, returning the value of the last non-nil form or nil if any is nil, stopping early on a nil result. For instance:

lisp

(and (numberp x) (> x 0) (1+ x))

(and (numberp x) (> x 0) (1+ x))

This avoids evaluating subsequent forms if the condition fails. or uses (or &rest forms), evaluating sequentially and returning the first non-nil value or nil if all are nil, short-circuiting on the first true. It complements and for alternatives:

lisp

(or (find item list) (error "not found"))

(or (find item list) (error "not found"))

Both preserve the lexical environment and can nest naturally. Finally, not is a simple function (not x), returning t if x is nil and nil otherwise, used for negation without side effects. It evaluates its argument fully, unlike the short-circuiting of and and or.

Metaprogramming

Macro Definition and Expansion

Macros in Common Lisp are defined using the defmacro form, which establishes a macro with a specified name in the global environment. The syntax is (defmacro name lambda-list [[declaration* | documentation]] normal-form*), where the lambda-list is a macro lambda list that can include destructuring and special parameters such as &optional, &rest, &key, &body, &whole, and &environment. The body consists of normal forms that, when the macro is invoked, are evaluated in an implicit block named after the macro to produce the expansion form, which replaces the original macro call during processing. The expansion of a macro form occurs through the macro function associated with its operator, which receives the entire form and a lexical environment as arguments, and returns a new form. Common Lisp provides macroexpand and macroexpand-1 to perform expansions programmatically. macroexpand-1 expands a form once if it is a macro form, returning the expanded form and a secondary value indicating whether expansion occurred; it does not recurse into the result. In contrast, macroexpand repeatedly applies macroexpand-1 until the form no longer expands, using the optional environment argument to account for lexical macros defined with macrolet or symbol-macrolet. Both functions respect the global variable *macroexpand-hook* to allow custom expansion behavior. Advanced macro lambda lists support &whole, which binds the entire macro call form to a variable for , and &environment, which binds the lexical environment to a variable for querying definitions during expansion. For instance, &environment env allows the macro body to parse the form using functions like parse-body while considering the scope. These features enable sophisticated code generation, such as in the example (defmacro dm2b (&whole form a (&whole b (c . d) &optional (e 5)) &body f &environment env) ... ), where form and env provide access to the full call and context. Macro expansion happens at processing time, specifically during compilation with compile-file or evaluation with eval, before the resulting form is executed or further compiled. For top-level forms, if the form is a macro call, its expansion is computed and processed in the same mode (compile-time or load-time), ensuring side effects like variable definitions occur appropriately. The macro body itself must be evaluable at this expansion time, as it runs in the compiler or evaluator's context rather than at runtime. A representative example is the once-only macro, which ensures macro arguments are evaluated exactly once to avoid side effects or inefficiency from repeated evaluation. It is defined as follows:

lisp

(defmacro once-only ((&rest names) &body body) (let ((gensyms (loop for name in names collect (gensym)))) `(let (,@(mapcar (lambda (g n) `(,g ,n)) gensyms names)) (let (,@(mapcar (lambda (g n) `(,n ,g)) gensyms names)) ,@body))))

(defmacro once-only ((&rest names) &body body) (let ((gensyms (loop for name in names collect (gensym)))) `(let (,@(mapcar (lambda (g n) `(,g ,n)) gensyms names)) (let (,@(mapcar (lambda (g n) `(,n ,g)) gensyms names)) ,@body))))

When used in another macro, such as (defmacro foo (a b) (once-only (a b) (list ,a ,b))), the expansion binds temporary gensyms to the values of aandb(evaluated once), then rebindsaandb` to those temporaries in the inner scope, ensuring single evaluation in the final . This technique addresses common pitfalls in macro writing, with hygiene concerns like variable capture typically handled separately.

Backquote and Quasiquotation

In Common Lisp, the backquote (also known as quasiquotation) provides a mechanism for constructing list-based data structures where parts of the structure are quoted literally while others are evaluated and inserted dynamically. The syntax begins with a backquote character (), which treats the following form as a template similar to a quoted form but allows for selective evaluation. Within this template, an unquote operator (,form) evaluates the specified form and inserts its value in place, while a splicing unquote (,@form) evaluates the form—expected to yield a list—and splices its elements into the surrounding list structure. This is defined in the ANSI Common Lisp standard, where backquote expansions must produce results that are equal` to the constructed form, with consistent side effects. For instance, given variables x bound to 1 and y bound to (2 3), the expression (list ,x ,@y) expands to (list 1 2 3), effectively building a list by inserting the value of x and splicing the elements of y. In contrast, without splicing, (list ,x ,y) would yield (list 1 (2 3)), preserving y as a sublist. These unquotes are evaluated exactly once during the expansion of the backquoted form, which occurs at macro-expansion time if the backquote appears in a macro body, or at read/eval time otherwise; this ensures compile-time construction where applicable, avoiding runtime overhead for static parts of the template. Nested quasiquotation allows for multi-level templates, useful in metaprogramming to generate code that itself contains quasiquoted forms. Expansions proceed from the innermost backquote outward, with the leftmost comma associating to the nearest enclosing backquote. For example, the expression (a ,,(+ 1 2)) first expands the inner (+(+ 1 2)) to 3, resulting in (a ,3), which upon further evaluation becomes (a 3). A more complex case like `(quasiquote ,',x), with x bound to a symbol such as foo, expands to (quasiquote (quote foo)), enabling the construction of quoting forms at a meta-level. This nesting behavior adheres to the standard's rule that undefined consequences arise only from misplaced splicing in non-list contexts, such as ,@form directly within a backquoted atom. To ensure uniqueness in generated code, particularly within macro expansions that use backquote, the gensym function creates fresh, uninterned symbols prefixed by a string (default "G") followed by an incrementing counter from the variable *gensym-counter*. These symbols, denoted in reader syntax as #:g0001 for example, avoid conflicts with user-defined variables when inserted into templates via unquote. For instance, in a macro generating a let-binding, (let ((,g (gensym))) `(let ((,g 42)) ,body)) produces a unique binding like (let ((#:g123 42)) body), evaluated at expansion time to safeguard against name capture. The gensym mechanism is integral to robust macro authoring, as uninterned symbols are guaranteed distinct across separate calls.

Hygiene and Variable Capture

In Common Lisp, macros can lead to variable capture, a situation where symbols introduced in the macro expansion unintentionally to variables in the surrounding lexical scope, altering the intended of the expanded code. This occurs because macro expansions are textual substitutions performed in the lexical environment where the macro is invoked, allowing macro-generated symbols to clash with user-defined bindings if not handled carefully. For instance, a naive macro definition that introduces a fixed like temp for an intermediate binding will capture any existing temp variable in the user's code, leading to incorrect results or errors. To prevent variable capture, Common Lisp programmers typically employ the gensym function, which generates a unique symbol guaranteed not to conflict with existing ones, often prefixed with G followed by a number (e.g., #:G1234). This approach ensures that macro-introduced variables remain local to the expansion without shadowing or being shadowed by user variables. An example is a buggy version of a when macro: (defmacro when-buggy (cond &body body) (if ,cond (,temp ,@body))), which fails if tempis already bound in the scope; the hygienic version uses(let ((temp (gensym))) ...)` to wrap the binding safely. Common Lisp's macro system is unhygienic by design, meaning it does not automatically rename variables to avoid capture as in Scheme; instead, must be maintained manually through techniques like gensym or explicit scoping with let and fresh symbols. For more advanced , developers can use symbol-macrolet to define local macros that rename symbols in a controlled way, though this is less common for capture avoidance than gensym. Libraries such as provide utilities like once-only, which combines gensym with argument evaluation control to further mitigate related issues in macro arguments, promoting safer expansions. A representative example of applying these techniques is defining a dolist-like macro without capture: (defmacro my-dolist ((var list) &body body) (let ((g-list (gensym))) (let ((,g-list ,list)) (let ((,var nil)) (loop while ,g-list do (setf ,var (pop ,g-list)) ,@body))))); here, g-listusesgensymto avoid conflicting with any userlist` variable. This ensures the expansion behaves independently of the surrounding environment. Best practices for ensuring hygiene include systematically testing macro expansions with macroexpand or macroexpand-all to inspect generated code for unintended bindings, often in conjunction with unit tests that verify behavior across different lexical contexts. This manual verification is essential, as Common Lisp prioritizes programmer control over automatic hygiene, allowing intentional capture when desired but requiring diligence to avoid bugs.

Condition Handling

Conditions as Objects

In Common Lisp, conditions are objects that encapsulate exceptional situations, errors, or other notable events during program execution. They are instances of the standardized class condition, which serves as the root of a type designed to facilitate extensible and type-based of these events. This object-oriented model allows conditions to inherit properties and behaviors from superclasses, enabling programmers to define custom condition types using define-condition while leveraging the built-in structure. The condition hierarchy includes key subclasses such as error (for general errors), warning (for non-serious notifications), and storage-condition (for resource exhaustion issues), among over 30 standardized types. For instance, error is a direct subtype of serious-condition, which itself subclasses condition, ensuring that serious conditions inherit the basic reporting capabilities while adding semantics for intervention. Non-serious conditions like warning and its subtype style-warning do not subclass serious-condition, allowing them to signal issues without mandating a halt in execution. This hierarchy supports subtype relationships that are not mutually exclusive, promoting flexible categorization. Signaling a condition involves creating an instance and announcing it via dedicated functions. The signal function constructs and raises a condition of type simple-condition by default (or the specified type), invoking any matching handlers; if none exist, it returns nil and execution continues uninterrupted. In contrast, error signals a serious condition (defaulting to simple-error) and, if unhandled, transfers control to the via invoke-debugger, preventing normal return. The cerror function similarly signals a correctable but provides a prompt, returning nil if the condition is continued through a restart option. These functions accept a datum (a condition class or instance) and optional arguments for initialization. Conditions often include slots for storing situation-specific data, accessible via reader functions. For example, instances of simple-condition (a common base for many built-in types) feature slots for a format control string and associated arguments, accessed respectively by simple-condition-format-control and simple-condition-format-arguments; these are initialized via keyword arguments to make-condition and default to suitable values if omitted. Custom condition types can define additional slots through define-condition, enhancing the object's descriptiveness. Serious conditions, such as those under error, differ from simple or warning conditions in that unhandled instances require interactive resolution, whereas warnings permit seamless continuation. To determine a condition's position in the hierarchy, the predicate typep is employed, testing membership in subtypes like (typep some-condition 'error). This enables runtime dispatch and matching in handling logic. Condition objects integrate with broader handling mechanisms, such as handlers, by allowing type-based selection without altering the core object structure.

Handlers and Restarts

In Common Lisp, handlers provide a mechanism for intercepting signaled conditions dynamically, allowing programs to respond to exceptional situations without relying solely on unwinding the stack. The condition system, designed to support interactive debugging and recovery, distinguishes between signaling a condition and establishing responses through handlers and restarts. Handlers are functions invoked when a matching condition is signaled, and they can choose to handle the condition or decline, enabling layered error management. The handler-case macro establishes a set of handlers around the evaluation of an expression, transferring control to the first matching if a condition is signaled during execution. Its syntax is (handler-case expression {error-clause}*), where each error-clause is of the form (typespec ([var]) declaration* form*); here, typespec identifies the condition type, var optionally binds the condition object, and the forms define the handler's response. If no matching is found, the signaled condition propagates to outer handlers or the . This macro unwinds the dynamic environment upon handler activation, ensuring clean separation from the original computation context. In contrast, the handler-bind macro binds handlers without automatic control transfer, allowing more flexible invocation via non-local exits like throw or invoke-restart. Its syntax is (handler-bind ({(type handler)}*) form*), where each handler evaluates to a function of one argument (the condition) that can inspect and decide whether to handle it, often by returning normally to continue searching outer bindings or performing recovery. This form is useful for establishing handlers that integrate with restarts, as the handler function operates in a scope where inner bindings are invisible, preventing recursive interference. Restarts complement handlers by offering predefined recovery options, such as retrying an operation or aborting it, which can be invoked programmatically or interactively from the . A restart consists of a function to execute upon , an optional name for identification, and optional and interactive functions for user presentation. They are established dynamically using macros like restart-case or restart-bind, with extent limited to the enclosing form's evaluation. The invoke-restart function calls the associated restart function with supplied arguments, provided the restart is active in the current dynamic environment; its syntax is (invoke-restart restart &rest arguments), and it may cause a non-local transfer of control, signaling a control-error if the restart is invalid. The restart-case macro simplifies restart establishment by wrapping an expression with named recovery clauses, each potentially prompting the user for input. Its syntax is (restart-case restartable-form {clause}*), where a clause is (case-name lambda-list [[ :interactive interactive-exp | :report report-exp | :test test-exp ]] declaration* form*); the :report option generates a string description (e.g., "Retry the operation"), and :interactive defines how to gather arguments interactively (e.g., via read). If a condition is signaled, the debugger lists available restarts with their reports, numbered for selection, allowing choices like abort (transferring to an outer context) or retry (re-executing the form). This enables interactive recovery without hardcoding all possibilities in the code. For instance, consider handling a division-by-zero error with a restart to supply a default value:

lisp

(restart-case (/ 10 0) (use-default () :report "Use 1 as the divisor instead" (/ 10 1)))

(restart-case (/ 10 0) (use-default () :report "Use 1 as the divisor instead" (/ 10 1)))

If the division signals an arithmetic-error, the restart can be invoked interactively or via (invoke-restart 'use-default), returning 10 instead of entering the . This example demonstrates how restarts provide graceful degradation, with the handler (if bound) able to select the restart based on context.

Object-Oriented Features

CLOS Classes and Instances

The Common Lisp Object System (CLOS) provides a mechanism for defining classes to encapsulate data in slots and creating instances of those classes as objects. Classes are primarily defined using the macro defclass, which specifies the class name, a list of direct superclasses, a list of slot specifiers, and optional class options. The resulting class is an instance of the metaclass standard-class by default, enabling full extensibility through subclassing and method specialization. The syntax for defclass is (defclass name (superclass-name*) (slot-specifier*) {class-option}*), where each slot-specifier can be a simple slot name or a list including options such as :initarg to associate a keyword with the slot for initialization, :initform to provide a default value form, and :accessor to automatically define accessors for reading and writing the slot. Superclasses inherit slots and options unless overridden, following the class precedence . Defining a class also establishes a corresponding type specifier of the same name, usable with typep. For instance, the following defines a point class inheriting from standard-object (implicitly), with two slots:

lisp

(defclass point () ((x :initarg :x :accessor x :initform 0) (y :initarg :y :accessor y :initform 0)))

(defclass point () ((x :initarg :x :accessor x :initform 0) (y :initarg :y :accessor y :initform 0)))

This creates reader/writer methods x and y, allows initialization via :x and :y keywords, and sets defaults to 0 if unspecified. Instances of a class are created using the make-instance, whose primary method has the (make-instance class &rest initargs &key &allow-other-keys). It allocates an uninitialized instance via allocate-instance and then invokes initialize-instance with the provided initargs, which are keyword-value pairs matching slot :initarg options. Unsupplied initargs lead to use of :initform values or leave slots unbound. The function returns the initialized instance. For the point class above:

lisp

(defvar p (make-instance 'point :x 1 :y 2))

(defvar p (make-instance 'point :x 1 :y 2))

This produces an instance where (x p) and (y p) return 1 and 2, respectively; if called as (make-instance 'point), both slots default to 0. Method dispatch on make-instance can be specialized for custom allocation or validation. Direct slot access and modification occur via the function slot-value, with syntax (slot-value instance slot-name), where slot-name is a . It returns the slot's value or invokes slot-missing if the slot does not exist in the instance's class or superclasses. While slot-value works on any instance, it bypasses accessors and is typically used only when no accessor is defined or for meta-level operations. Continuing the example:

lisp

(slot-value p 'x) ; => 1 (setf (slot-value p 'x) 3)

(slot-value p 'x) ; => 1 (setf (slot-value p 'x) 3)

Accessors like x and y are preferred, as they are s allowing method extension without altering slot structure. Unbound slots signal via slot-unbound when read. Object initialization is managed by the initialize-instance, invoked by make-instance after allocation, with (initialize-instance instance &rest initargs &key &allow-other-keys). Its system-supplied primary method for standard-object delegates to shared-initialize to fill slots: it processes initargs to set corresponding slots, applies :initform forms (evaluated in the class's lexical environment) for unspecified slots, and signals an for invalid initargs unless allowed by methods. Users may define :after methods on initialize-instance for post-initialization actions, such as validation or computed slots, without overriding the primary slot-filling logic. In the point example, if an :after method were defined, it would execute after slots are set from initargs or :initform. Default initargs can also be specified at the class level via the :default-initargs option in defclass, appending to explicit ones during initialization. CLOS includes predefined classes for all built-in types, categorized by metaclass: standard classes are instances of standard-class and support full CLOS operations like subclassing and instancing, while built-in classes (instances of built-in-class) represent primitive types with optimized implementations and restrictions—subclassing via defclass, creating generalized instances via make-instance, or accessing slots via slot-value on them signals an error. For example, the number class is a built-in class, corresponding to the number type specifier, and its instances (e.g., integers, floats) cannot be extended as CLOS objects in this manner. This integration ensures compatibility between type-based and class-based programming while preserving efficiency for core types.

Generic Functions and Methods

In Common Lisp's Common Lisp Object System (CLOS), s provide a mechanism for polymorphic behavior, allowing the same function name to invoke different s based on the classes or identities of its arguments. A is distinct from ordinary functions in that it does not have a single body of code; instead, it is associated with one or more methods, each defining a specific implementation. When a is called, the system selects applicable methods using a dispatch process that considers argument specializers, sorts them by precedence, and combines them according to a specified type, typically the standard method combination by default. The macro defgeneric is used to declare a generic function, optionally specifying its lambda list and various options that govern its overall behavior. The syntax is (defgeneric function-name gf-lambda-list [[option | {method-description}*]]), where function-name is a symbol naming the generic function, and gf-lambda-list defines the parameter structure that all methods must conform to for congruence. Key options include :argument-precedence-order, which lists required parameters to establish a custom order for sorting applicable methods during dispatch (each parameter must appear exactly once); :method-combination, which specifies how selected methods are combined (e.g., standard or short); and :documentation for adding a docstring. If no generic function exists with the given name, defgeneric creates one of class standard-generic-function; otherwise, it updates the existing one, potentially removing prior methods. Methods for a generic function are defined using the macro defmethod, which associates a body of code with specific argument specializers. The syntax is (defmethod function-name {method-qualifier}* specialized-lambda-list [[declaration* | documentation]] form*), where specialized-lambda-list allows specialization on required parameters via class names or EQL specializers for exact object matches. For class-based specialization, a parameter is written as (var class-name), restricting applicability to arguments that are instances of that class or its subclasses. For EQL specialization, the form (var (eql eql-specializer-form)) applies the method only when the argument is eql to the evaluated form, enabling dispatch on specific values rather than types. If no generic function exists, defmethod creates a default one; otherwise, it adds or replaces the method, ensuring lambda list congruence with the generic function's. The method body executes within an implicit block named by function-name. Dispatch occurs when a generic function is invoked: the system first identifies applicable methods whose specializers match the argument classes or identities, then sorts them using the generic function's precedence order. This process is handled internally by the generic function compute-applicable-methods, which takes a generic-function and function-arguments as inputs and returns a sorted list of applicable method objects, ordered from most specific to least specific based on the class precedence list and any custom :argument-precedence-order. The sorted methods are then combined—typically via primary, before, after, and around methods in standard combination—to form the effective method that executes the call. If no methods are applicable, the system invokes the generic function no-applicable-method as a fallback, passing the original generic-function and function-arguments. Its default method signals an error of type error, but users can define custom methods on no-applicable-method to provide alternative handling, such as returning a default value or prompting for input. This mechanism ensures robust error recovery in polymorphic dispatch. For illustration, consider a for drawing shapes:

(defgeneric draw (shape)) (defmethod draw ((shape circle)) (format t "Drawing a circle~%")) (defmethod draw ((shape (eql 'square))) (format t "Drawing a square~%"))

(defgeneric draw (shape)) (defmethod draw ((shape circle)) (format t "Drawing a circle~%")) (defmethod draw ((shape (eql 'square))) (format t "Drawing a square~%"))

Calling (draw (make-instance 'circle)) dispatches to the first method, while (draw 'square) uses the EQL-specialized second method, demonstrating type- and value-based polymorphism.

Multimethods and Inheritance

In the Common Lisp Object System (CLOS), multimethods enable multiple dispatch, where the selection of a method for a generic function depends on the classes or values of multiple arguments at runtime. This is achieved through the defmethod macro, which allows parameter specializers to be specified for required arguments beyond the first. For instance, a method can be defined to dispatch based on both arguments being of specific classes, as in (defmethod greet ((person1 person) (person2 child)) ... ), contrasting with single-dispatch systems that specialize only the first argument. CLOS supports through the defclass macro, where a class can specify multiple direct superclasses in its definition, such as (defclass employee (person company-thing) ...). The class precedence list (CPL), computed via a that respects local precedence order and paths, determines the order of for slots and methods. In cases of slot conflicts across superclasses, the slot from the most specific class in the CPL takes precedence, with characteristics like allocation and initform derived from the highest-precedence specifier; for example, a local instance slot shadows a shared class slot from a superclass. Method combinations in CLOS dictate how applicable methods are ordered and invoked, with the standard method combination being the default. This includes primary methods (the core functionality) combined with auxiliary methods qualified as :before (executed before primaries for setup), :after (executed after for cleanup), and :around (wrapping primaries and other auxiliaries for additional control). Short-form combinations, like those for built-in functions such as + or max, simply order primary methods by specificity without auxiliaries, while long-form combinations allow custom specification of qualifiers and ordering rules. To illustrate multimethods and , consider a for computing areas of geometric s. Define a base class and subclasses:

(defclass shape () ()) (defclass circle (shape) ((radius :initarg :radius :accessor radius))) (defclass rectangle (shape) ((width :initarg :width :accessor width) (height :initarg :height :accessor height)))

(defclass shape () ()) (defclass circle (shape) ((radius :initarg :radius :accessor radius))) (defclass rectangle (shape) ((width :initarg :width :accessor width) (height :initarg :height :accessor height)))

A area can then use on a and a unit type:

(defgeneric area (shape unit)) (defmethod area ((s circle) (u meter)) (* pi (expt (radius s) 2))) (defmethod area ((s [rectangle](/page/Rectangle)) (u meter)) (* (width s) (height s)))

(defgeneric area (shape unit)) (defmethod area ((s circle) (u meter)) (* pi (expt (radius s) 2))) (defmethod area ((s [rectangle](/page/Rectangle)) (u meter)) (* (width s) (height s)))

Here, dispatch selects the appropriate method based on the classes of both shape and unit, leveraging so that subclasses of shape could further specialize behavior.

Modularity

Packages and Symbols

In Common Lisp, packages provide a mechanism for managing namespaces by grouping symbols and controlling their accessibility, thereby enabling modular code organization and avoiding name conflicts across different parts of a program. A package maps strings (names) to and defines which symbols are internal (private to the package) or external (publicly accessible). Symbols are the primary objects in this system, serving as identifiers for variables, functions, and other entities, with each symbol belonging to exactly one home package unless uninterned. The defpackage macro is used to define a new package, specifying its name, the packages it uses (making their external symbols accessible without qualification), and the symbols it exports (designating them as external for use by other packages). For example, (defpackage :my-package (:use :cl) (:export "FUNCTION1" "VAR2")) creates a package named MY-PACKAGE that inherits symbols from the COMMON-LISP package (aliased as :cl) and exports the symbols FUNCTION1 and VAR2. This declaration ensures that only explicitly exported symbols are visible to dependent packages, promoting encapsulation. Options like :nicknames can also assign alternative names to the package for convenience. To switch the current package or import symbols, macros such as in-package and use-package are employed. The in-package macro sets the current package to the one named by its argument, affecting subsequent symbol creation and reading; for instance, (in-package :my-package) makes MY-PACKAGE the default for interning new symbols. Meanwhile, use-package adds the external symbols of specified packages to the current package's accessibility list, potentially resolving or flagging name conflicts. Symbols are interned into a specific package using the intern function, which takes a string and an optional package designator, returning the symbol and a boolean indicating if it is new (T) or already existing (NIL). For example, (intern "FOO" :cl-user) interns the symbol FOO in the CL-USER package, creating it if it does not exist. This process ensures symbols are uniquely associated with their home package, facilitating qualified references like CL-USER::FOO for internal symbols or CL-USER:FOO for external ones. The KEYWORD package hosts self-quoting symbols known as keywords, which are prefixed with a colon (e.g., :foo) and evaluate to themselves, commonly used as named arguments or enum-like values. All keywords are automatically exported from the KEYWORD package, with interning into KEYWORD making a symbol self-evaluating. For instance, :foo is equivalent to (intern "FOO" :keyword) and remains constant across sessions. To handle name conflicts when importing symbols, the shadowing-import function allows a symbol to be added to a package as internal, overriding any existing accessible symbol with the same name without error. This is useful in defpackage via the :shadowing-import-from option, such as (defpackage :my-package (:shadowing-import-from :other "CONFLICT")), which imports and shadows CONFLICT from the OTHER package. Shadowed symbols remain inaccessible in the importing package unless explicitly referenced with qualification, preserving namespace integrity.

Pathnames and Loading

In Common Lisp, pathnames provide a portable for representing locations, independent of the underlying operating system. Every pathname consists of six components: host, device, directory, name, type, and version. The host identifies the file system (such as a network host), the device specifies a physical volume, the directory denotes the hierarchical path, the name is the base filename, the type indicates the file extension (e.g., "lisp" for source files), and the version handles numbering for file revisions. This structure ensures that programs can manipulate file references without embedding host-specific details. Pathname objects are created using the make-pathname function, which accepts keyword arguments for each component. For instance, the expression (make-pathname :host "localhost" :directory '(:absolute "src")) constructs a pathname for the "src" directory on a local host. This function allows fine-grained control over pathname construction, with unspecified components defaulting to nil. Pathnames can also be derived from strings via parse-namestring or merged with defaults using merge-pathnames. The load function incorporates the contents of a file specified by a pathname designator into the current environment, executing forms sequentially from source files or directly loading precompiled . It supports both source files (typically with type "lisp") and compiled files, with options for , printing progress, handling non-existence, and specifying external formats (though the latter is ignored for compiled files). For conditional module loading, the require function checks if a module (identified by a name) has already been provided—via the provide function, which adds the module to the *modules* list—and loads associated files only if necessary, using an optional list of pathnames or an implementation-defined mechanism. Logical pathnames enable abstract file references that map to physical paths, often configured via site-specific translations. The translate-pathname function performs this mapping by matching a source pathname against a "from-wildcard" and constructing a corresponding physical pathname based on a "to-wildcard" template, replacing wild or nil fields with source components. This supports flexible remapping, such as translating logical directories to physical ones, with behavior varying by implementation to accommodate file system conventions. To verify file existence before loading, the probe-file function tests a pathname designator and returns its truename (a physical pathname) if the file exists, or nil otherwise; it signals an error for wild pathnames. This is essential for robust file handling, as it interacts directly with the host . Beyond ANSI Common Lisp facilities, ASDF (Another System Definition Facility), introduced in 2001 as a successor to earlier tools, serves as the for managing complex software projects. System definitions in ASDF, typically in .asd files using defsystem, declare components (files or subsystems), dependencies, and loading order, enabling automatic resolution and incremental loading of interdependent modules. For example, (asdf:load-system "my-system") compiles and loads all necessary parts while respecting declared prerequisites, facilitating portable builds across implementations.

Execution Model

Interpreter Behavior

The Common Lisp interpreter primarily interacts with users through the read-eval-print loop (REPL), an interactive mechanism that repeatedly reads Lisp forms from an input , evaluates them in the current environment, and prints the resulting values to an output . This loop enables and exploratory programming, as forms are processed immediately without requiring compilation or file loading. The REPL primarily reads input from the special variable *standard-input*, which is typically bound to the terminal input . The variable *query-io* designates the for interactive queries and user responses during , often also bound to the terminal by default. In the REPL, top-level forms—those entered directly at the prompt—are evaluated sequentially from left to right, with the primary value of each form printed before proceeding to the next. This behavior mimics the semantics of the PROGN special form, which evaluates a sequence of subforms in order and returns the value of the last one, but each top-level form is treated independently, allowing side effects from earlier forms to affect later ones within the same lexical and dynamic environment. For example, defining a variable in one form makes it available for use in subsequent forms during the session. Debugging during interpreted execution is supported by facilities that pause and inspect the computation. The BREAK function signals a simple-error and invokes the , suspending normal evaluation and presenting an interactive prompt for examination and , which is particularly useful in the REPL for halting at arbitrary points. Similarly, the INSPECT function provides an interactive interface for exploring object structures, allowing users to navigate slots, components, and values in a step-by-step manner, with implementation-defined commands for actions like modifying parts of the object. The standard TRACE macro instruments function calls to print entry and exit information, aiding in tracking execution flow without altering code, and can be applied selectively to specific functions during interpreted runs. The standard STEP macro provides single-stepping of interpreted code, pausing after each form evaluation for user inspection. The REPL environment can be queried and modified through special variables that reflect the current state. For instance, *package* holds the current package, determining symbol resolution for unqualified names, while *readtable* points to the active readtable, which governs the parsing of input syntax. These variables allow developers to inspect and adjust the evaluation context dynamically, such as switching packages mid-session with (in-package :cl-user) or customizing reader behavior.

lisp

;; Example REPL interaction demonstrating sequential evaluation CL-USER> (defvar *x* 10) *X* CL-USER> (+ *x* 5) 15 CL-USER> *x* 10 ;; Using BREAK for pausing (defun test-break () (break "Paused for inspection") (* 2 3)) ;; TRACE example (trace my-function) (my-function arg1 arg2) ; Prints entry/exit traces

;; Example REPL interaction demonstrating sequential evaluation CL-USER> (defvar *x* 10) *X* CL-USER> (+ *x* 5) 15 CL-USER> *x* 10 ;; Using BREAK for pausing (defun test-break () (break "Paused for inspection") (* 2 3)) ;; TRACE example (trace my-function) (my-function arg1 arg2) ; Prints entry/exit traces

Compiler Optimizations and FASL Files

Common Lisp provides mechanisms for compiling code to improve performance, primarily through the functions compile and compile-file. The compile function takes a name and an optional lambda expression, producing a compiled function object from the input; if no name is provided, it compiles the form and returns the resulting function along with a name derived from the source. This allows individual functions or expressions to be compiled interactively or within code, enabling mixed interpreted and compiled execution where compiled functions can invoke interpreted ones and vice versa. In contrast, compile-file processes an entire source file, transforming its contents into implementation-dependent binary data stored in a Fast Load (FASL) file, which is a compact, platform-specific format optimized for rapid loading into the Lisp environment. FASL files facilitate efficient distribution and reuse of compiled code, as they contain pre-optimized machine code that bypasses re-parsing and re-compilation during loading. Compiler optimizations in Common Lisp are guided by declarations that influence tradeoffs among qualities such as speed, safety, space, and compilation speed. The optimize declaration specifies priorities for these qualities on a scale from 0 (minimal) to 3 (maximal), allowing programmers to direct the compiler toward faster execution at the potential cost of debugging aids or memory usage; for instance, (declare (optimize (speed 3) (safety 0))) prioritizes maximum speed while disabling runtime checks. Such declarations enable the compiler to apply transformations like inlining small functions or eliminating bounds checks when safety is reduced. Type declarations, such as (declare (type fixnum x)), provide hints that the compiler uses for type inference, propagating type information across expressions to enable optimizations including dead code elimination and specialized code generation. In implementations like SBCL, which inherits advanced type inference from CMUCL, the compiler infers types conservatively from declarations and context, allowing it to generate efficient machine code by avoiding generic operations and removing unreachable branches based on type constraints. For values that must be computed at load time rather than , Common Lisp offers the *load-time-value* macro, which evaluates its form during file loading and substitutes the result as a constant in the compiled code. This mechanism supports constants that depend on runtime conditions, such as platform-specific values, while ensuring the computation occurs only once per load; the form is evaluated at for compile but deferred to load time in compile-file outputs. In FASL files, this preserves the constant's value without requiring re-evaluation, contributing to both and portability. Certain implementations, such as SBCL, support cross-compilation to target different architectures from a host system, producing FASL files or executables for platforms like or without running code on the target during the build process. This is achieved through a multi-phase bootstrap involving a host to generate portable intermediate representations, followed by target-specific code generation, enabling deployment to embedded or remote environments.

Illustrative Code

Fundamental Examples

Common Lisp's fundamental examples highlight its core syntax for interaction, data structures, arithmetic, scoping, and runtime evaluation. These snippets demonstrate the language's prefix notation, where operators precede arguments enclosed in parentheses, and its support for immediate execution in an interactive environment known as the REPL (Read-Eval-Print Loop). Such examples underscore Common Lisp's design for and exploration, as defined in the ANSI standard. A basic output example uses the function to produce formatted text on the standard output . The directive ~A inserts a string argument without modification.

lisp

(format t "Hello, World!")

(format t "Hello, World!")

This evaluates to NIL but prints "Hello, World!" followed by a . To incorporate user input, combine with read, which prompts for and returns an object from the input .

lisp

(let ((name (read))) (format t "Hello, ~A!" name))

(let ((name (read))) (format t "Hello, ~A!" name))

Executing this reads a value (e.g., the symbol World), binds it to name, and prints "Hello, World!" The let special operator establishes lexical bindings in parallel, ensuring the initial forms are evaluated before any body form references them. Lists form a foundational data type in Common Lisp, built from cons cells via the cons function, which constructs a pair with a car (first element) and cdr (rest).

lisp

(cons 'a (cons 'b nil)) ; => (A B)

(cons 'a (cons 'b nil)) ; => (A B)

This creates a proper by consing elements onto nil, the empty . For manipulation, nreverse destructively reverses a sequence like a , modifying the original structure to improve efficiency by reusing cells.

lisp

(let ((lst (list 1 2 3))) (nreverse lst) lst) ; => (3 2 1)

(let ((lst (list 1 2 3))) (nreverse lst) lst) ; => (3 2 1)

Here, nreverse alters lst in place, returning the reversed ; the let binding localizes the list to avoid global side effects. Numeric computation exemplifies , a natural fit for Lisp's function definitions. The of a non-negative nn, defined as n!=n×(n1)!n! = n \times (n-1)! with 0!=10! = 1, can be computed recursively using defun to define a named function and if for conditional branching.

lisp

(defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1)))))

(defun factorial (n) (if (<= n 1) 1 (* n (factorial (- n 1)))))

Evaluating (factorial 5) yields 120. An iterative variant uses tail recursion with an accumulator parameter to avoid stack growth in optimized implementations.

lisp

(defun factorial-iter (n &optional (acc 1)) (if (<= n 1) acc (factorial-iter (- n 1) (* n acc))))

(defun factorial-iter (n &optional (acc 1)) (if (<= n 1) acc (factorial-iter (- n 1) (* n acc))))

This computes the same result but builds the product iteratively from the base case upward. Variable binding with let enables local computation, such as the sum of squares of two numbers, where bindings shadow any outer variables.

lisp

(let ((x 3) (y 4)) (+ (* x x) (* y y))) ; => 25

(let ((x 3) (y 4)) (+ (* x x) (* y y))) ; => 25

The arithmetic operators * and + handle exact computation, promoting to bignums if needed. Dynamic code evaluation leverages intern to create or retrieve a symbol from a string in the current package, followed by eval to compute a form in the dynamic environment.

lisp

(let ((sym (intern "FOO"))) (setf sym 42) (eval sym)) ; => 42

(let ((sym (intern "FOO"))) (setf sym 42) (eval sym)) ; => 42

intern returns the symbol FOO; setf binds it dynamically; eval then yields its value. This illustrates Common Lisp's homoiconicity, treating code as data, though eval ignores lexical bindings.

Algorithmic Implementations

Common Lisp's support for and higher-order functions makes it particularly well-suited for implementing classic algorithms in a concise and expressive manner. This section presents complete, self-contained examples of such implementations, demonstrating how features like defstruct for structures, recursive functions, and built-in utilities for lists and can be leveraged for algorithmic problem-solving. These examples focus on standard approaches without relying on external libraries, emphasizing clarity and efficiency where appropriate.

Quick Sort

Quick sort is a that selects a and partitions the into sublists of elements less than or equal to the pivot and greater than the pivot, then recursively sorts the sublists. In Common Lisp, a recursive implementation can use operations like and predicates to achieve this partitioning without modifying the original in place, resulting in a functional style that aligns with Lisp's list-processing heritage. The following code defines a function that handles empty lists as the base case and uses the first element as the pivot for simplicity.

lisp

(defun quicksort (lst) (if (null lst) nil (let* ((pivot (car lst)) (rest (cdr lst)) (less-or-equal (remove-if-not (lambda (x) (<= x pivot)) rest)) (greater (remove-if (lambda (x) (<= x pivot)) rest))) (append (quicksort less-or-equal) (list pivot) (quicksort greater)))))

(defun quicksort (lst) (if (null lst) nil (let* ((pivot (car lst)) (rest (cdr lst)) (less-or-equal (remove-if-not (lambda (x) (<= x pivot)) rest)) (greater (remove-if (lambda (x) (<= x pivot)) rest))) (append (quicksort less-or-equal) (list pivot) (quicksort greater)))))

This implementation has average time complexity O(n log n). It can be tested with (quicksort '(3 1 4 1 5 9 2 6)), yielding (1 1 2 3 4 5 6 9).

Binary Search Tree

A binary search tree (BST) is a tree data structure where each node has at most two children, with keys in the left subtree less than the node's key and keys in the right subtree greater. 's defstruct provides a straightforward way to define node structures with slots for key, value, left, and right children. Insertion recursively traverses the tree to find the correct position, updating the structure mutably for efficiency, while search follows a similar path until matching the key or reaching a leaf. The code below implements these operations, assuming integer keys for comparison.

lisp

(defstruct node (key nil) (value nil) (left nil) (right nil)) (defun insert (tree key value) (if (null tree) (make-node :key key :value value) (let ((current-key (node-key tree))) (cond ((< key current-key) (setf (node-left tree) (insert (node-left tree) key value)) tree) ((> key current-key) (setf (node-right tree) (insert (node-right tree) key value)) tree) (t (setf (node-value tree) value) tree))))) (defun search (tree key) (when tree (let ((current-key (node-key tree))) (cond ((= key current-key) (node-value tree)) ((< key current-key) (search (node-left tree) key)) (t (search (node-right tree) key))))))

(defstruct node (key nil) (value nil) (left nil) (right nil)) (defun insert (tree key value) (if (null tree) (make-node :key key :value value) (let ((current-key (node-key tree))) (cond ((< key current-key) (setf (node-left tree) (insert (node-left tree) key value)) tree) ((> key current-key) (setf (node-right tree) (insert (node-right tree) key value)) tree) (t (setf (node-value tree) value) tree))))) (defun search (tree key) (when tree (let ((current-key (node-key tree))) (cond ((= key current-key) (node-value tree)) ((< key current-key) (search (node-left tree) key)) (t (search (node-right tree) key))))))

To use, create an empty tree as nil, insert with (insert nil 5 "five"), and search with (search *tree* 5). These operations run in O(h) time, where h is the tree height, ideally O(log n) for balanced trees.

Exponentiation by Squaring

Exponentiation by squaring reduces the number of multiplications needed to compute base raised to an exponent by recursively squaring the base and halving the exponent, handling even and odd cases separately. This recursive approach in Common Lisp exploits the language's tail recursion optimization potential (though not guaranteed) and built-in arithmetic predicates like evenp. The function below computes powers efficiently, with base case for exponent 0 and recursive cases for even exponents (square and halve) and odd (multiply by base and recurse on even part). It assumes non-negative exponents.

lisp

(defun pow (base exp) (if (= exp 0) 1 (if (evenp exp) (pow (* base base) (/ exp 2)) (* base (pow (* base base) (/ (1- exp) 2))))))

(defun pow (base exp) (if (= exp 0) 1 (if (evenp exp) (pow (* base base) (/ exp 2)) (* base (pow (* base base) (/ (1- exp) 2))))))

For example, (pow 2 10) returns 1024 using only 4 multiplications instead of 9 in naive . The is O(log exp), making it suitable for large exponents.

Birthday Paradox Simulation

The birthday paradox illustrates that in a group of 23 people, the probability of at least two sharing a exceeds 50%, assuming 365 days and uniform distribution. A simulation estimates this by running many trials, generating random birthdays for a fixed group size, and checking for duplicates using set operations. In Common Lisp, random generates birthdays, and remove-duplicates with a test detects collisions if the unique count is less than the group size. The code below simulates for group size n over trials iterations, returning the estimated probability.

lisp

(defun birthday-simulation (n trials) (let ((collisions 0)) (dotimes (_ trials) (let* ((birthdays (loop repeat n collect (1+ (random 365)))) (unique (remove-duplicates birthdays :test #'=))) (when (< (length unique) n) (incf collisions)))) (/ collisions trials)))

(defun birthday-simulation (n trials) (let ((collisions 0)) (dotimes (_ trials) (let* ((birthdays (loop repeat n collect (1+ (random 365)))) (unique (remove-duplicates birthdays :test #'=))) (when (< (length unique) n) (incf collisions)))) (/ collisions trials)))

Executing (birthday-simulation 23 10000) approximates 0.507, close to the theoretical 0.5073, with more trials improving accuracy. This leverages Common Lisp's loop constructs for efficient .

Graph Traversal

(DFS) explores a graph by following one path as far as possible before , useful for and . Representing the graph with a where keys are nodes and values are adjacency lists allows O(1) neighbor access. A recursive DFS in Common Lisp uses the current path to avoid cycles, recursing on unvisited neighbors until the target or all paths are exhausted. The example below implements path-finding DFS from start to end, returning the path if found or nil.

lisp

(defun dfs (graph current end path) (if (eq current end) (reverse path) (dolist (neighbor (gethash current graph)) (unless (member neighbor path :test #'eq) (let ((found (dfs graph neighbor end (cons neighbor path)))) (when found (return-from dfs found))))) nil))

(defun dfs (graph current end path) (if (eq current end) (reverse path) (dolist (neighbor (gethash current graph)) (unless (member neighbor path :test #'eq) (let ((found (dfs graph neighbor end (cons neighbor path)))) (when found (return-from dfs found))))) nil))

First, build the graph: (defvar *graph* (make-hash-table)) (setf (gethash 'A *graph*) '(B C)). Then (dfs *graph* 'A 'D (list 'A)) finds a path if it exists. Time complexity is O(V + E) for vertices V and edges E.

Comparisons

With Other Lisp Dialects

Common Lisp differs from Scheme in several fundamental aspects of its design and features. While both languages descend from the Lisp family, Scheme adopts purely lexical scoping for variables, where bindings are resolved based on the textual structure of the code, as defined in its standards. In contrast, Common Lisp supports both lexical and dynamic scoping, with lexical as the modern default but dynamic scoping available for legacy compatibility and specific use cases like special variables. This dual approach in Common Lisp provides greater flexibility for interactive development and global bindings but can introduce complexities absent in Scheme's minimalist, purely lexical model. Regarding object-oriented programming, Common Lisp includes the Common Lisp Object System (CLOS), a comprehensive, multi-paradigm OO framework integrated into the ANSI standard, supporting , multimethods, and generic functions. Scheme, by contrast, offers minimal built-in support for OO, relying on user-defined macros or libraries for object systems, without a standardized OO mechanism in reports like R5RS or R7RS. Error handling further distinguishes the languages: Common Lisp's condition system allows signaling conditions as objects, establishing handlers, and offering interactive restarts without stack unwinding, enabling non-local recovery and . Scheme typically employs exceptions or continuations for error propagation, lacking a native condition-restart model and often relying on implementation-specific extensions. Compared to , Common Lisp emphasizes native compilation and mutable data structures as core features. Clojure runs on the (JVM), leveraging Java's ecosystem for while hosting Lisp semantics atop it. This JVM foundation contrasts with Common Lisp's native implementations, which compile directly to without a intermediary. For concurrency, Clojure provides (STM) as a primary mechanism, promoting immutable data to avoid race conditions and simplify parallel programming. Common Lisp, however, relies on mutable data and threading libraries like Bordeaux-Threads for concurrency, supporting locks and channels but without built-in STM, which can lead to more explicit synchronization in multithreaded applications. Common Lisp and Racket diverge in modularity, contracts, and macro systems. Racket employs a module system where code is organized into self-contained units with explicit imports and exports, facilitating language extensions and submodules. Common Lisp uses packages for namespacing across files, with via pathnames, but lacks Racket's submodule for finer-grained composition. On contracts versus types, Racket's contracts provide dynamic checks on function arguments and results at boundaries, enforcing properties like domain-specific predicates without static enforcement. Common Lisp offers optional type declarations for optimization and static checking in some implementations, but these are hints rather than enforced contracts, focusing on compile-time efficiency over runtime boundary validation. For macros, Racket builds in hygienic expansion by default, automatically avoiding unintended symbol captures through syntax objects. Common Lisp macros are non-hygienic by design, requiring manual via gensym or explicit scoping to prevent name clashes, offering greater control but demanding more care in complex expansions. Standardization highlights another contrast, particularly with Scheme. Common Lisp is governed by the ANSI X3.226-1994 standard, a comprehensive 1,100-page specification ensuring portability across implementations. Scheme's standards, such as R5RS (1998) and R7RS (2013), are more concise reports—R5RS spans about 50 pages—focusing on a minimal core with libraries added modularly in R7RS-large, but lacking the breadth of Common Lisp's integrated features. Interoperability between Common Lisp and these dialects remains limited. For , integration is possible via Armed Bear Common Lisp (ABCL), a JVM-hosted Common Lisp implementation that enables calling (and thus Clojure) code through foreign function interfaces (FFI), though full bidirectional Lisp-Lisp interaction requires custom bridging. Direct FFI or embedding is feasible but uncommon due to differing runtimes and semantics.

With Imperative and Functional Languages

Common Lisp stands out among programming languages by embracing a multi-paradigm approach, seamlessly integrating procedural, object-oriented, and functional styles within a single, dynamically typed framework. This flexibility contrasts with more specialized imperative languages like , which primarily emphasize with rigid class-based inheritance, or functional languages like , which enforce purity and immutability to avoid side effects. In Common Lisp, developers can mix imperative sequences of statements for , leverage the Common Lisp Object System (CLOS) for extensible object interactions, and employ higher-order functions and closures for functional composition, all without syntactic or semantic barriers that constrain single-paradigm languages. Compared to Python, another dynamically typed language often used for scripting and rapid development, Common Lisp shares similarities in runtime type checking and but diverges in its handling of multiple results and recovery. Python typically returns multiple values as tuples, which require explicit unpacking and can lead to allocation overhead in performance-critical code, whereas Common Lisp's multiple values mechanism allows functions to return several results efficiently without constructing intermediate data structures, optimized by implementations like CMU Common Lisp to avoid consing. For handling, Python relies on exceptions that unwind the stack and halt execution unless caught, while Common Lisp's condition system supports non-local transfers, restarts, and handler bindings, enabling more interactive and resumable management without disrupting program flow. In relation to Java, an imperative object-oriented language with static typing and a virtual machine runtime, Common Lisp's CLOS provides a more generic and extensible model through multimethods, where method dispatch considers all arguments rather than just the receiver object, allowing behavior to vary based on combinations of types without subclassing hierarchies. This contrasts with Java's single-dispatch virtual methods tied to class inheritance, which can lead to brittle designs in polymorphic scenarios. Additionally, while Java's JVM garbage collection can introduce unpredictable stop-the-world pauses, especially in large heaps, tuned Common Lisp implementations like SBCL employ generational collectors with configurable thresholds and low-latency modes, minimizing interruptions for real-time or interactive applications through techniques like write barriers and parallel marking. Relative to , a pure functional that prohibits mutable state and side effects to ensure , Common Lisp adopts an impure functional style, permitting mutation via assignment and side-effecting operations alongside pure functions, which facilitates imperative efficiency in domains like while retaining functional abstractions. Haskell's type classes enable polymorphic behavior through implicit dictionaries resolved at , promoting type-safe overloading, but Common Lisp counters with hygienic macros that transform code structure at expansion time, offering greater power for custom syntax without relying on . A key aspect of Common Lisp's expressiveness stems from its , where code is represented as lists that can be manipulated like , enabling the creation of domain-specific s (DSLs) through macros that extend the itself. In contrast, Python supports DSLs primarily through external libraries or string-based parsing, which lacks the seamless integration and compile-time guarantees of Lisp macros, often resulting in less ergonomic or performant custom syntax.

Implementations

High-Performance Native Compilers

(SBCL) is a prominent high-performance native for Common Lisp, derived from the Common Lisp (CMUCL) system. It compiles Lisp source code directly to , enabling efficient execution on various architectures including and ARM64. SBCL incorporates a conservative generational garbage collector (GENCGC), which scans the stack and registers conservatively to identify pointers, allowing for effective without precise root tracking at the cost of occasional retention of non-pointer data as garbage. Additionally, SBCL features advanced , performing more extensive analysis than many other Common Lisp compilers to derive variable types from and declarations, thereby enabling aggressive optimizations. Clozure Common Lisp (CCL), formerly known as OpenMCL, is another optimizing native emphasizing speed and platform integration, particularly on Apple ecosystems. It supports native operating system threads, facilitating concurrent programming without the overhead of threads. CCL runs on macOS (x86-64), including support for via its Objective-C bridge for Cocoa integration, and offers fast startup times due to its efficient image-based loading mechanism. Its garbage collector is precise, generational, and compacting, which minimizes fragmentation and reduces pause times compared to conservative approaches. Both SBCL and CCL employ sophisticated compiler optimizations, such as type-derived inlining, where inferred types allow the to inline generic functions with specialized implementations, avoiding runtime dispatch overhead. For interoperability with C libraries, SBCL provides the SB-ALIEN (FFI), which enables direct calls to C functions and manipulation of C data structures without intermediate marshalling, supporting type-safe conversions for primitives and aggregates. CCL offers a similar FFI with fast compilation of foreign calls, integrated seamlessly with its threaded runtime. These optimizations contribute to high in compute-intensive tasks. In benchmarks focused on numeric computations, such as matrix operations and spectral-norm calculations, SBCL and CCL demonstrate superior runtime over interpreter-based Common Lisp systems like CLISP, often achieving speeds within 1-5x of optimized code due to native compilation and type-aware optimizations. For instance, in , SBCL executes numeric workloads efficiently, outperforming pure interpreters by orders of magnitude while maintaining Lisp's dynamic features. Recent developments in SBCL include version 2.5.10, released on October 27, 2025, which features enhancements such as improved platform support and handling of specific operations on ARM64, broadening its applicability to mobile and embedded devices. These updates, along with ongoing type propagation refinements, continue to elevate SBCL's performance profile for .

Portable and Interpreter-Based Systems

Portable and interpreter-based Common Lisp implementations prioritize standards adherence, cross-platform compatibility, and seamless integration into diverse environments, making them suitable for scripting, embedding, and development where high performance is secondary to flexibility. These systems often leverage interpreters or hybrid compilation strategies to ensure broad portability without relying on platform-specific optimizations. Embeddable Common Lisp (ECL) is an implementation designed for embedding capabilities into C-based applications, featuring an interpreter and a that translates Lisp code to C for native execution. It complies with the ANSI X3J13 Common standard, supporting core features such as CLOS, conditions, and loops, while offering high portability across operating systems including , Windows, macOS, and various Unix variants, as well as architectures like x86, , and PowerPC. ECL's C backend facilitates integration into larger software systems, allowing Lisp code to be compiled into shared libraries or executables alongside C components, which is particularly useful for extending applications with dynamic scripting or AI functionalities. GNU CLISP, part of the GNU Project, is a bytecode-interpreted implementation emphasizing ease of use for scripting and interactive development. It adheres to the ANSI Common Lisp standard (INCITS 226-1994), providing a full-featured environment that runs on multiple platforms such as , BSD, macOS, Windows via , and Solaris, though its last official release was version 2.49 in July 2010 and it is no longer actively maintained. CLISP's interpreter enables and shell-like scripting, with built-in support for modules, , and a read-eval-print loop suitable for command-line tools. Armed Bear Common Lisp (ABCL) runs on the (JVM), offering both an interpreter and a compiler that generates for seamless interoperability with ecosystems. As a conforming ANSI Common Lisp , it supports features and extends them with Java integration via mechanisms like the (JSR-223) and the Java Support System (JSS) for calling Java methods and accessing classes directly from Lisp code. ABCL enhances portability by leveraging the JVM's cross-platform nature, deploying on Windows, , macOS, and other JVM-supported environments, making it ideal for mixed-language applications in enterprise settings. These implementations uphold ANSI Common Lisp compliance, ensuring code portability through features like logical pathnames, which abstract file paths into a platform-independent syntax defined by the X3J13 committee to facilitate consistent file handling across diverse host systems. Logical pathnames use a standardized namestring format (e.g., host:directory;filename.type) that translates to physical paths at runtime, mitigating differences in operating system conventions and enabling reliable resource location in portable applications. Integration tools like ASDF (Another System Definition Facility) further bolster portability by providing a standardized build system for defining, loading, and managing dependencies across implementations, including ECL, CLISP, and ABCL. ASDF organizes code into modular systems, handles compilation and loading uniformly, and incorporates UIOP for OS- and implementation-agnostic utilities, allowing developers to create build configurations that work consistently regardless of the underlying Lisp runtime. This facilitates cross-implementation testing and deployment, emphasizing ease of use in heterogeneous environments.

Recent and Niche Implementations

In recent years, the Common Lisp community has seen the emergence of specialized implementations targeting modern computing environments, including for browser-based execution. The Embeddable Common Lisp (ECL) project has advanced its port, enabling Common Lisp code to run natively in web browsers via compilation. This allows interactive development and execution of Lisp applications directly in the browser, as demonstrated by ports of applications like Maxima for symbolic computation and multiplayer games using SDL2 bindings. SICL represents a niche, modular approach to implementing Common , designed as a collection of portable, implementation-independent modules written primarily in Common Lisp itself. Developed by Robert Strandh, SICL emphasizes from existing systems and serves as a toolkit for creating custom Lisp environments, with components like a new loop macro implementation and condition system that can be mixed with other runtimes. Its modular structure facilitates research and experimentation, distinguishing it from monolithic implementations. Clasp stands out as an AI-focused implementation, leveraging for native code generation and providing seamless interoperability with C++ libraries, which is particularly useful for and scientific computing tasks. This allows Common Lisp developers to integrate with high-performance C++ frameworks like those in molecular modeling or tensor operations, compiling mixed Lisp-C++ code via link-time optimization. Clasp's design supports extensions for domains requiring tight integration with numerical libraries, enhancing its applicability in AI workflows. Recent updates include version 2.7.0, released January 21, 2025, which added package lock support. Community efforts highlighted at the European Lisp Symposium (ELS) in 2024 and 2025 have further spotlighted niche extensions, including GPU acceleration. Talks at ELS 2025 discussed applications in Common Lisp, building on libraries like CL-GPU, which translates Lisp subsets to kernels for on hardware. These presentations emphasized integrating Lisp with GPU extensions for numerical tasks, alongside ports like SBCL to embedded platforms such as the , reflecting ongoing innovation in specialized runtimes.

Applications

Established Software Systems

Common Lisp has been instrumental in developing several enduring software systems, particularly in , symbolic computation, and commercial applications. The project, initiated in 1984 by at Microelectronics and Computer Technology Corporation (MCC), represents a seminal AI effort to construct a comprehensive encoding common-sense reasoning. Implemented primarily in SubL, a of Common Lisp developed by Cycorp, employs mechanisms to derive new knowledge from its of over 500,000 concepts and millions of assertions, enabling applications in expert systems and semantic reasoning. In symbolic mathematics, Maxima stands as a prominent derived from the original developed at MIT in the late 1960s and early 1970s. Ported to Common Lisp as part of the DOE-MACSYMA codebase in the , Maxima supports advanced symbolic manipulation, including integration, differentiation, and , with its core engine leveraging Lisp's list-processing capabilities for expression handling. It remains a foundational tool for mathematical research and education, distributed under the GNU General Public License. NASA's Remote Agent Experiment (RAX), conducted in 1999 aboard the spacecraft, demonstrated autonomous spacecraft control using Common Lisp-based software. The Remote Agent system integrated model-based planning, execution, and fault diagnosis modules, developed with tools like Harlequin LispWorks, to manage mission goals without ground intervention over distances of millions of kilometers. This flight-proven application highlighted Lisp's suitability for real-time, embedded AI in space exploration. Commercially, —launched in the mid-1990s by Paul Graham and Robert Morris—pioneered software-as-a-service for online stores, powering what became Yahoo Store after its 1998 acquisition by Yahoo. Written almost entirely in Common Lisp, Viaweb's backend handled generation and logic, processing thousands of stores with efficient code that scaled on modest hardware, demonstrating Lisp's productivity in early . Similarly, ITA Software's fare search engine, deployed in the late 1990s and powering Orbitz's backend since 2001, utilized over 200,000 lines of Common Lisp code across implementations like CMUCL and Allegro CL to optimize complex airline pricing algorithms, influencing the travel industry. Among infrastructure tools, ASDF (Another System Definition Facility), introduced in the early 2000s, serves as the for defining and building Common Lisp systems, managing dependencies, compilation, and loading across implementations. Complementing it, Quicklisp, released in beta in 2010 by Zach Beane, revolutionized package management by automating the discovery, download, and installation of over 1,500 libraries from a centralized repository, streamlining development workflows.

Contemporary Uses and Ecosystem

In web development, Common Lisp continues to support robust server-side applications through libraries like Hunchentoot, a mature and toolkit that handles HTTP/1.1 features such as , persistent connections, and SSL support, enabling the creation of dynamic websites with easy access to request parameters and session management. Complementing this, the Weblocks framework (now maintained as Reblocks) facilitates the building of single-page applications (SPAs) via a widgets-based approach, generating AJAX updates without requiring client-side and integrating seamlessly with templating tools like for server-rendered UIs. These tools underscore Common Lisp's viability for modern web projects, as evidenced by active maintenance and resources updated as recently as January 2025. In and , Common Lisp leverages libraries such as CLML, a high-performance statistical package that provides implementations for classifiers (e.g., ), clustering algorithms (e.g., k-means and ), support vector machines, and time-series analysis, optimized for large-scale data processing on implementations like SBCL. Integration with external frameworks is enabled through CFFI bindings like those in the tf library, which wraps TensorFlow's C APIs to allow Common Lisp programs to load and utilize models for tasks. The 2025 European Lisp Symposium (ELS) highlighted this domain's vitality with sessions including a keynote on "Is Lisp Still Relevant in the New Age of AI?" exploring historical and contemporary roles, a research paper on implementations in Common Lisp, and a round-table discussion on Lisp's contributions to AI. For game development, the engine stands out as a modular system tailored for 3D graphics and interactive applications, offering components for scene management, rendering pipelines, input handling, and physics simulation, all built to leverage Common Lisp's expressiveness for rapid prototyping and deployment. Updated through August 2025, Trial has powered commercial titles like the action RPG Kandria, demonstrating its production readiness. Additionally, in 2025, the (SBCL) compiler and runtime were ported to the platform, as presented at the European Lisp Symposium, opening possibilities for Lisp-based applications on gaming consoles. The Common Lisp ecosystem thrives via Quicklisp, the primary library manager, which as of 2025 distributes over 1,500 libraries and received updates in June 2025 and October 2024, facilitating easy installation and dependency resolution across implementations. REPL-driven development remains a hallmark, praised in the 2024 Common Lisp Community Survey for enabling interactive experimentation and iterative refinement, which boosts productivity in complex domains. The community sustains this momentum, with r/Common_Lisp on showing sustained engagement through discussions on tooling and projects, contributing to its growth as a hub for practitioners. LispNYC meetups, held monthly since 2002, foster collaboration among New York-area developers focused on Lisp variants including Common Lisp, with virtual and in-person events promoting knowledge sharing. Adoption in 2025 is driven by features like macros, which enable domain-specific languages for AI tasks such as reasoning and model generation, as discussed in ELS 2025 proceedings.

References

  1. https://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Quicksort
Add your contribution
Related Hubs
User Avatar
No comments yet.