Anatomy of a Common Lisp file
Common Lisp is a standardized, multi-paradigm Lisp with interactive development, native compilation, macros, an object system, and a powerful condition-and-restart system.
File extensions: .lisp, .lsp, .cl
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Common Lisp file
Comment
A semicolon starts a line comment; more semicolons conventionally mark broader scope.
One or more semicolons comment out the remainder of a line. By convention, ; annotates a nearby expression, ;; explains a local section, and ;;; introduces a top-level definition or file section. Common Lisp also has reader comments: #| ... |# nests for blocks, and #; tells the reader to skip the next complete form: useful when temporarily disabling a balanced expression.
Package definition
Creates a namespace and declares what it uses and exports.
defpackage creates or updates a package at compile/load time. (:use :cl) makes the standardized Common Lisp symbols accessible, and (:export :main) marks MAIN as part of this package’s public interface. Packages organize symbol names, not source files or object modules. A system definition tool such as ASDF separately describes which files make up a larger program.
Package selection
Makes a package the reader’s current namespace for following forms.
in-package changes the value of *package*, controlling where unqualified symbols read later in the file are interned or resolved. It does not import a file and it does not wrap following forms in a lexical block. Placing it near the top prevents definitions from accidentally landing in the implementation’s default CL-USER package.
Global special variable
defparameter defines a dynamically scoped global and assigns its value.
defparameter always assigns the supplied initial value when the form is evaluated. The surrounding asterisks are a naming convention that warns readers the variable is special (dynamically bindable) rather than an ordinary lexical local. defvar differs by preserving an existing bound value, which makes it useful for user-configurable state during interactive development.
Structure definition
defstruct defines a record-like type plus constructor and accessor functions.
(defstruct greeting text enthusiasm) creates the type greeting, a constructor named make-greeting, a predicate, a copier, and accessors such as greeting-text. This generated protocol is ordinary Common Lisp functionality, so code calls the accessors like any other functions. More elaborate object models use the Common Lisp Object System (defclass and generic functions).
Function definition (`defun`)
Binds a named function with a parameter list and one or more body forms.
defun installs a global function definition. Its body evaluates from left to right, and the value of the final form becomes the function’s return value. No dedicated return statement is required. Common Lisp keeps a symbol’s function binding separate from its variable binding, which is why the same printed symbol can name both without conflict.
Lambda list
Describes required, optional, rest, and keyword parameters.
(&optional (name *default-name*)) makes name optional and supplies an expression for its default. Common Lisp lambda lists can also use &rest to gather extra arguments, &key for named arguments, and &aux for auxiliary bindings. The same vocabulary appears in anonymous lambda functions, methods, and many macro definitions, with specialized additions for those contexts.
Lexical binding (`let`)
Creates local variable bindings for the forms in its body.
let evaluates each initializer, establishes the local bindings in parallel, and evaluates its body in that lexical environment. let* is the sequential variant, allowing each later initializer to refer to earlier bindings. A local name declared special, including the conventional *earmuffed* globals, receives a dynamic binding instead, visible to functions called during the binding’s lifetime.
LOOP macro
Expresses iteration with a rich clause-oriented mini-language.
loop repeat 2 do ... expands at macro-expansion time into lower-level control forms. The extended loop language supports collection, destructuring, stepping, filtering, and aggregation without requiring explicit recursion. Because loop is a macro, its syntax is not evaluated like an ordinary function call. Lisp macros receive source forms and produce new forms for the compiler to process.
Condition handler
handler-case handles signaled conditions and selects a recovery expression.
handler-case evaluates a protected form and, if a matching condition is signaled, transfers control to the corresponding clause. The caught condition is an object that can be inspected or reported. Common Lisp’s broader condition system separates signaling, handling, and recovery. Restartable protocols can offer recovery choices without forcing the low-level signaling code to decide which choice is appropriate.
Function call and prefix notation
Places the operator first, followed by its arguments inside one list.
(format t "~A~%" message) calls format with three arguments. The first position of an evaluated list selects a function, macro, or special operator; remaining forms supply arguments according to that operator’s rules. Parentheses are therefore structural syntax rather than optional decoration. Data uses the same list notation, but quoting ('(...)) prevents evaluation and yields the list itself.
Official Common Lisp site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.