Common Lisp is a general-purpose, multi-paradigm programming language that descends from the Lisp family. Standardized in the late 1980s and early 1990s (ANSI X3.226-1994), it supports functional, object-oriented, and procedural styles. Known for its powerful macro system, dynamic typing, interactive development environment, and a comprehensive standard library, Common Lisp has been used in artificial intelligence, computer algebra, web development, and systems programming. Its design emphasizes flexibility and extensibility, enabling programmers to mold the language to the problem domain.

1 History and standardization

1.1 Origin in earlier Lisp dialects

Common Lisp emerged from the need to unify several divergent Lisp dialects that had proliferated during the 1970s and early 1980s. The most influential predecessors included MacLisp (MIT), Interlisp (BBN and Xerox PARC), and Lisp Machine Lisp (Symbolics, LMI, Xerox). Each dialect had its own syntax, scoping rules, and standard library, making code portability difficult. In 1981, a group led by Guy L. Steele Jr. began drafting a common language that would combine the best features of these dialects, resulting in the publication of *Common Lisp the Language* in 1984 (first edition) and a revised second edition in 1990.

1.2 The ANSI Common Lisp standard

The formal standardization effort began in 1986 under the American National Standards Institute (ANSI) committee X3J13. After years of deliberation, the standard was approved in 1994 as ANSI X3.226-1994 (often referred to as ANSI Common Lisp). This standard solidified the language’s core—including its object system (CLOS), condition system, macro facility, and extensive data types—and provided a definitive reference for implementers and users.

1.3 Later revisions and ongoing evolution

While no new ANSI standard has superseded the 1994 specification, the Common Lisp community has continued to evolve the language through de facto extensions and libraries. Key efforts include the Common Lisp HyperSpec (an online version of the standard with commentary), the Common Lisp Community Specification (CLCS) draft for additional features like concurrency, and widespread adoption of utilities such as Alexandria and Bordeaux Threads. The language remains stable, with modern implementations adding optimizations and support for new platforms.

2 Language overview

2.1 Syntax and evaluation model

2.1.1 S-expressions and reader

Common Lisp uses S-expressions (symbolic expressions) as its primary syntactic form. The reader converts textual input into internal data structures such as symbols, numbers, lists, and vectors. S-expressions are typically parenthesized prefix-notation lists, where the first element denotes an operator or function name. The reader is extensible via reader macros, allowing custom syntax for different domains.

2.1.2 Top-level forms and REPL

A Common Lisp program consists of a sequence of top-level forms. The Read-Eval-Print Loop (REPL) is the standard interactive interface: it reads an expression, evaluates it, prints the resulting value, and loops. This environment enables incremental development and immediate feedback, a hallmark of Lisp programming.

2.2 Data types

2.2.1 Numbers (integers, floats, ratios, complex)

Common Lisp provides a rich numeric tower. Integers are arbitrary-precision (bignums) within machine limits. Floats include single- and double-precision formats. Ratios represent exact rational numbers, and complex numbers are constructed from real and imaginary parts. Arithmetic operations automatically promote types.

2.2.2 Characters and strings

Characters are atomic objects representing a single glyph, with support for Unicode (depending on implementation). Strings are vectors of characters, adjustable and fillable. The language includes a comprehensive set of character and string operations, such as concatenation, case conversion, and searching.

2.2.3 Symbols and packages

Symbols are unique identifiers used for variable names, function names, and data objects. Each symbol belongs to a package, which serves as a namespace. Packages enable modularity and control over name visibility, supporting import, export, and use. Symbol internment ensures efficient comparison (by identity).

2.2.4 Lists, vectors, and hash tables

Lists are the classic Lisp data structure, built from cons cells (pairs). They support traversal, filtering, and mapping via built-in functions. Vectors are one-dimensional arrays of fixed size; adjustable vectors can grow. Hash tables provide key-value storage with configurable equality tests (e.g., EQ, EQL, EQUAL) and handling of collisions.

2.2.5 Structures and arrays

Structures are user-defined data types with named slots, created via DEFSTRUCT. Arrays are general multidimensional containers; they can be specialized for efficiency (e.g., for machine integers or floats). Multidimensional arrays support row-major storage and generic access operations.

2.3 Control flow

2.3.1 Conditionals (IF, COND, CASE)

Common Lisp includes IF for binary branching, COND for multi-way conditional chains, and CASE for dispatch on a value’s identity or type. These forms return values and can be nested arbitrarily. Boolean values are NIL (false) and any non-NIL (true).

2.3.2 Iteration (DO, LOOP, DOTIMES)

Iteration is expressed through DO, a general loop construct; DOTIMES, a simple numeric loop; and LOOP, a powerful extensible macro supporting iteration over collections, numeric ranges, and custom accumulation. LOOP uses a keyword-based syntax derived from the “Loop” macro, enabling concise, readable loops.

2.3.3 Exception handling via conditions and restarts

The Common Lisp condition system provides a sophisticated mechanism for signaling and handling exceptional situations. Unlike traditional exception systems, it separates the detection of a problem (signaling a condition) from the recovery (invoking a restart). Restarts are established proactively; handlers can choose to resume execution at a known point, retry, or propagate. This design supports robust error recovery and interactive debugging.

2.4 Functions and closures

2.4.1 Lambda expressions and lexical scoping

Functions are first-class objects created with LAMBDA or named via DEFUN. Common Lisp uses lexical scoping: variable references are resolved based on the lexical (static) structure of the code. Closures capture the lexical environment, allowing functions to retain access to variables even after the outer scope exits.

2.4.2 Higher-order functions and function objects

Higher-order functions such as MAPCAR, REDUCE, and FILTER accept functions as arguments. Function objects can be passed, stored, and invoked via FUNCALL and APPLY. The functional operators COMPLEMENT, CONSTANTLY, and CURRY facilitate functional composition. Anonymous functions written with LAMBDA are common.

2.5 The Common Lisp Object System (CLOS)

2.5.1 Classes and instances

CLOS is a dynamic object-oriented system defined in the ANSI standard. Classes are defined with DEFCLASS, specifying slots and their properties (e.g., accessors, initargs). Instances are created via MAKE-INSTANCE. Slots can be directly accessed using SLOT-VALUE or through generated reader/writer methods.

2.5.2 Generic functions and multi-methods

Instead of message-passing, CLOS uses generic functions: functions that dispatch based on the types of all required arguments (multi-methods). Methods are defined with DEFMETHOD, specializing one or more parameters. Generic functions support before, after, and around methods for method combination, enabling efficient separation of concerns.

2.5.3 Inheritance and method combination

CLOS supports multiple inheritance: a class can inherit from several superclasses. Inheritance order is determined by a linearization algorithm (typically C3). Method combination controls how applicable methods are assembled into a single effective method. Standard method combination includes primary, before, after, and around methods; custom combinators can be defined.

3 Macros and metaprogramming

3.1 Expression-based macro mechanism

Macros in Common Lisp operate on unevaluated S-expressions, transforming source code before compilation or evaluation. The macro function receives the entire macro call as a list and returns new code. This allows arbitrary syntactic extensions, such as new control structures, domain-specific languages, and compile-time computations.

3.2 Hygiene and macro-writing utilities (WITH-...)

Traditional Common Lisp macros are “procedural” and can inadvertently capture or shadow variables (non-hygienic). To manage complexity, the language provides utilities like MACROEXPAND, GENSYM (to generate unique symbols), and WITH- macros (e.g., WITH-OPEN-FILE) that encapsulate resource management. Libraries such as Alexandria offer hygienic macro helpers.

3.3 Reader macros and custom syntax

Reader macros are functions attached to a dispatch character (e.g., #', #(, #.) that modify how the reader interprets input. They can introduce entirely new syntax, such as custom literals for data structures or embedded DSLs. The SET-DISPATCH-MACRO-CHARACTER function allows programmers to extend the reader arbitrarily.

4 Programming environment and tools

4.1 Interactive development (REPL, SLIME, SLY)

The REPL is the core interactive tool. SLIME (Superior Lisp Interaction Mode for Emacs) and its successor SLY provide a full IDE: source-code navigation, debugger integration, inspector, compilation feedback, and a REPL running inside Emacs. These tools enable rapid, incremental development by allowing pieces of a live system to be modified and recompiled without restart.

4.2 Compilation and optimization

4.2.1 Compiler macros and type declarations

Common Lisp compilers (e.g., SBCL, CCL) perform both bytecode and native compilation. Compiler macros allow the programmer to define transformations that the compiler applies for optimization. Type declarations with DECLARE or THE help the compiler generate efficient code, especially for numeric operations, by reducing runtime type checking.

4.2.2 Performance tuning and profiling

Profiling tools such as SB-SPROF (in SBCL) and time macros measure execution speed and memory usage. The COMMON-LISP package provides DISASSEMBLE to inspect generated machine code. Implementations often support optimization policies (speed, safety, space, debug) that trade off runtime safety for performance.

5 Standard library and extensions

5.1 Input/output streams and file system

The I/O system uses streams for reading and writing to files, terminals, and network sockets. Binary and character streams are supported. WITH-OPEN-FILE and WITH-OPEN-STREAM manage resource acquisition and release. The PATHNAME abstraction handles file naming, and DIRECTORY lists files.

5.2 Data serialization (read/print)

S-expressions can be serialized to text using PRINT (which produces machine-readable output) and read back with READ. This extends to arbitrary data through the *PRINT-* variables (e.g., *PRINT-ARRAY*, *PRINT-CIRCLE*). For binary serialization, libraries like CL-CONSTANTA or custom solutions exist.

5.3 Foreign function interface (CFFI, UFFI)

CFFI (C Foreign Function Interface) provides a portable way to call C functions and access C data structures from Common Lisp. It handles type conversion, memory management, and callbacks. UFFI (Universal Foreign Function Interface) is an older alternative; CFFI is now the de facto standard.

5.4 Concurrency and parallelism

The ANSI standard does not specify concurrency. De facto libraries include Bordeaux Threads (thread creation, synchronization with locks and condition variables) and lparallel (a parallel programming library with futures, worker pools, and data-flow parallelism). Some implementations (e.g., SBCL) provide native threads with a global lock (GIL-like) in earlier versions; recent releases support true parallelism.

6 Implementations

6.1 Open-source implementations (SBCL, Clozure CL, ECL)

SBCL (Steel Bank Common Lisp): a high-performance implementation derived from CMUCL, featuring a native compiler, efficient garbage collection, and extensive optimization support. – Clozure CL (CCL): originally Macintosh Common Lisp, now cross-platform, with a fast compiler and good support for concurrency. – ECL (Embeddable Common Lisp): a Common Lisp targeting C and capable of being embedded into C programs; it compiles Lisp to C via a bytecode interpreter or a native compiler.

6.2 Commercial and historical implementations (Allegro CL, LispWorks)

Allegro Common Lisp: a commercial implementation from Franz Inc., offering a graphical IDE, multiprocessing, and enterprise features (e.g., ORB, database connectivity). – LispWorks: a commercial implementation by LispWorks Ltd., bundled with a GUI builder, a debugger, and support for multiple operating systems. – Historical: Symbolics Genera (a Lisp Machine operating system), MacLisp, Interlisp, and others laid the groundwork for modern implementations.

7 Applications and influence

7.1 Use in AI and knowledge representation

Common Lisp was the primary language for early AI systems such as MYCIN, MACSYMA, and expert system shells. Its symbolic capabilities, interactive development, and powerful macro system made it ideal for rule-based reasoning, symbolic mathematics, and natural language processing. Modern AI libraries often use Lisp for knowledge representation and reasoning.

7.2 Modern web frameworks and scripting

Frameworks like Hunchentoot (a web server) and Caveman (a web application framework) support web development. Paul Graham’s use of Common Lisp for Viaweb demonstrated its potential for dynamic web applications. Build tools and scriptable systems (e.g., the Quicklisp package manager) rely on Lisp’s highly interactive environment.

7.3 Impact on language design (macros, GC, interactive development)

Common Lisp pioneered or popularized several concepts now common in other languages: – Macros: Lisp’s macro system directly influenced meta-programming in languages like Julia, Elixir, and Scheme. – Garbage Collection (GC): Generational and incremental GC techniques were first refined in Lisp implementations. – Interactive development: The REPL, image-based programming, and live debugging have inspired modern IDEs in languages like Clojure, Smalltalk, and Python (with IPython). – Multiple dispatch: CLOS’s generic functions and multi-methods influenced languages such as Julia and Dylan.