
\documentclass[12pt]{article}
\usepackage[T2A,OT1]{fontenc}
\usepackage[default]{cantarell}
\usepackage[a4paper, top=20mm, bottom=20mm, left=20mm, right=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[russian, english]{babel}
\usepackage{tabu}
\usepackage{hyperref}
\usepackage{parskip}
\usepackage{graphicx}
\usepackage{tabularx}
\usepackage[normalem]{ulem}
\usepackage{float}
\floatstyle{boxed}
\restylefloat{figure}
\usepackage{setspace}
\onehalfspacing
\author{Artyom Bologov \href{mailto:lisp-design-patterns@aartaka.me}{(email)}}
\date{\today}
\title{Lisp Design Patterns}
\makeatletter
\def\endenv{\expandafter\end\expandafter{\@currenvir}}
\makeatother
\begin{document}
\maketitle

\begin{figure}[h!]
\includegraphics[width=\textwidth,height=\textheight,keepaspectratio]{./assets/lizard-tail.jpg}
\caption{Fractal lizard showing us a proper way to design layered software.}\end{figure}

There was
\href{https://reddit.com/r/lisp/comments/19c9vn6/how_to_architect_large_lisp_codebase}{this question on how to design Lisp software}
on r/lisp.
The answers mostly falling into one of

\begin{itemize}\item “Try and see.”
\item “Use a general approach applicable to any language.”
\item “Do a REPL-driven development.”
\item “Build a functional style layered architecture.”
\end{itemize}

The last response is quite close to acceptable architecture advice.
But, I believe, neither (except for maybe the REPL answer) does justice to Lisp.
The Lisp family of languages have many features and architectural approaches.
Even though these patterns differ from the normie ones, they still are this: patterns.
In this post, I’m listing some of the general patterns that I’ve seen in mine and others’ code.
From more generic (pun intended) to the local ones.

I’m deliberately ignoring OOP patterns.
Lispy Object-Oriented systems (CLOS, EIEIO, GOOPS) might be strange and powerful.
But most of the techniques from other OOP languages apply to them nonetheless.
So let’s leave the space for more Lisp-specific patterns!

But to tip my hat to the OOP/Gang of Four people, I’ll start from one of their ideas:

\section*{Extension Points} \label{extension-points}

There’s an odd pattern among the ones Java programmers preach:
\href{https://medium.com/leadpixel/extension-points-306ecd271a5}{Extension Point}.
Most likely no one understands what exactly that pattern means.
It’s just “a part of your design open to later extension.”
But if anything suits the “Extension Point” name, it’s Lisp code.

All the patterns below are a certain kind of extension point.
Varying levels of complexity, assorted dialects, diverse features.
But still, these are some of the things that make Lisp software flexible and loveable.

\section*{By Contract: Protocols and Back-ends (CL, Clojure)} \label{protocols}

This top-level pattern is quite universal.
My friend recently raged about the overabundance of meaningless interfaces in Java and C#.
Golang is built around the idea of interfaces.
C++ boasts virtual classes.
Clojure allows to define \verb|defprotocol|-s
or multiple versions of
\href{https://clojure.org/reference/special_forms#def}{\verb|defn|-s}
and ensure their plasticity.

Common Lisp protocols usually come in the form of \verb|defgeneric| declarations.

\begin{itemize}\item One defines a generic function.
\item But they don’t define any implementation.
\item Down the line, they write code conforming to the contract this generic function established.
\item Further down the line, they can write another implementation.
\item And either of implementations might be drop-in alternatives for the downstream user.
\end{itemize}

A perfect example of this approach is Shinmera’s work
like
\href{https://github.com/Shirakumo/cl-mixed}{cl-mixed} with its heaps of back-ends.
Another example is JSON-parser-agnostic
\href{https://nyxt.atlas.engineer/article/njson.org}{NJSON}
(shameless plug: I designed it.)
Here’s how a protocol definition looks there:

\begin{figure}[h!]\begin{verbatim}
(defgeneric decode-from-stream (stream)
  (:method (stream)
    (signal 'decode-from-stream-not-implemented))
  (:documentation "Decode JSON from STREAM.
Specialize on `stream' to make NJSON decode JSON."))
\end{verbatim}\caption{Protocol generic requiring at least one implementation}\end{figure}

This type of architecture enforces a structure on the back-ends, while being adaptable to back-end swapping.
And this type of generalization also allows for...

\section*{Monkey Patching (Emacs Lisp, CL)} \label{monkey-patching}

If one can write a back-end and plug it into the software...
Then, well, anyone can do that.
Even the user.
Even if the software is quite intricate.
If it’s a mature enough Lisp, it likely has

\begin{itemize}\item Function overrides (any Lisp),
\item Method overrides (any OOP Lisp),
\item Advices (Emacs, some CL implementations, Clojure),
\item :around methods (CL),
\item Slot listeners (CL and some other OOP Lisps, likely),
\item <i>et cetera</i>, <i>et cetera</i>
\end{itemize}

This downstream-override approach is colloquially known as “monkey patching.”
It’s a basis for Aspect-Oriented Design, suggested by Gregor Kiczales.
The idiomatic example of that design is exactly what the OP of the Reddit post was asking about: logging.
In the the Aspect-Oriented-friendly application, logging can be added at any moment, just as an advice / override / \verb|:around| function.
No need to put \verb|console.log| all over the place, just make a special method and wrap your code into it!

Another example of this approach might be a home-brewed event system in Nyxt
(even though I left the team, I still love the code we’ve done together),
based on \verb|(setf slot) :around| methods in Common Lisp.
The logic is: once the slot is \verb|setf|-ed, invoke a function updating the UI:

\begin{figure}[h!]\begin{verbatim}
(defmethod (setf url) :around (value (buffer document-buffer))
  (call-next-method)
  (set-window-title))
\end{verbatim}\caption{Example of event-invoking setter method}\end{figure}

\section*{Hooks (Emacs, CL, and others)} \label{hooks}

Thanks to
\href{https://reddit.com/user/arthurno1}{u/arthurno1}
for
\href{https://reddit.com/r/lisp/comments/19fh0xx/comment/kjmuwt3/?context=3}{mentioning this}!

Hooks are variables / slots / objects effectively storing a list of functions (usually called “handlers”).
Once something happens, the functions stored inside the hook are called.
Emacs utilizes hooks as one of the main extension points, having hooks for:

\begin{itemize}\item Emacs initialization, closing, saving and other editor-specific actions.
\item Events.
\item Mode toggling.
\item And likely some other purposes I can’t recall.
\end{itemize}

Hooks may be said to be a subset/superset of the monkey-patched listeners.
But they are too conventional and useful to ignore.

\section*{Fail Fast, Fail Often: Conditions (CL)} \label{conditions}

I always \verb|panic!| when I see an error in a non-Lisp language.
Because this error usually means stack unwinding at best or program abort at worst.
So there’s a reason to fear errors and
\verb|try| to prevent them in any way possible.

Not in Lisp.
Not in Common Lisp, in particular.
CL has this Condition System thingie that errors are part of.
Restarts are a notable feature of CL conditions that most other exception / error / condition systems lack.
Restarts are basically pieces of code you can invoke from the debugger to fix the error.

\begin{itemize}\item Server returned an error code?
Just \verb|restart| to try again!
\item Something broke the code, but you don’t want to deal with it now?
Just \verb|ignore| or \verb|continue| to ignore it.
\item A typo made a wrong value to be passed to a function?
Just \verb|use-value| and replace the faulty argument.
\end{itemize}

All the computation is effectively paused until the user chooses a restart to resume it.
Which can happen at an arbitrary moment or not even happen at all.

The point partially applies to other Lisps, because of REPL-driven development.
Even if something goes wrong, one’s just dropped into another level of debug loop.
While in there, one can redefine the broken functions and restart the computation.

Or ignore the error and \verb|abort| out into toplevel REPL or out of it even.

\section*{Shared State: Dynamic Variables (Emacs Lisp, CL)} \label{dynamic-variables}

Ah, the dreaded global thread-unsafe state!
Even though most Lisps left the idea of dynamic scope behind, some still have it.
Common Lisp and Emacs Lisp both utilize dynamic variables... productively.
One particular example might be CL’s printer system.
There are
\href{https://www.lispworks.com/documentation/HyperSpec/Body/f_wr_pr.htm}{more than 15 globals}
which influence printing.

Which sounds like a disaster, right?
Modifying global state just to change local logic sounds painful.

Luckily, the main use for dynamic variables is not setting them directly.
Binding them lexically with \verb|let| is a much more widespread pattern.
It allows one to override the behavior these variables define, without making risky modifications of the shared state!

Shameless plug again: my
\href{https://github.com/aartaka/graven-image}{Graven Image} library.
\href{https://codeberg.org/aartaka/?q=trivial}{(UPD Oct 2025: functionality mostly moved to Trivial Toplevel libs.)}
There’s no custom printing in it, and all the APIs rely on the built-in functions like
\verb|print| for it.
At times, this makes the output look extremely inconsistent or outright scary.
As an intended use-case, the output of Graven Image functions is intended to be altered via printer variables:

\begin{figure}[h!]\begin{verbatim}
(defmethod gimage:apropos* :around (string \&optional package external-only docs-too)
  (let ((*print-case* :downcase)
        (*print-level* 2)
        (*print-lines* 2)
        (*print-length* 10))
    (call-next-method)))
\end{verbatim}\caption{An example method overriding (see previous sections) printer variables}\end{figure}

\section*{Pythonique: Keywords and Destructuring (CL, Guile, Clojure)} \label{keywords}

I’m not even sure whether I should include that: it’s too low-level.
But dynamic variables are quite atomic (pun not intended) too, so here goes nothing.

Programming in Guile, I often miss CL’s function keyword.
There’s
\verb|lambda*|/\verb|define*| for that, but it sucks that keywords are not enabled by default.

\begin{figure}[h!]\begin{verbatim}
(define* (jsc-class-make-constructor
          class #:key (name %null-pointer) (callback #f)
          (number-of-args (if callback (procedure-maximum-arity callback) 0)))
  "Create a constructor for CLASS with CALLBACK called on object initialization..."
  ...)
\end{verbatim}\caption{Use of \verb|define*| to make functions with keywords in Guile}\end{figure}

There are several places keywords are vital:

\begin{itemize}\item Non-trivial function behavior: \verb|dex:get| has 21 keyword arguments because HTTP is hard
and \verb|\&optional| arguments just don’t cut it.
\item Heavily Object-oriented code: \verb|apply| over \verb|make-instance| is one’s best friend.
\item Structure constructors are using keywords for arguments.
\item Other side of the OOP-FP spectrum: destructuring of arguments / EDN / data in Clojure.
There’s even a dedicated destructuring syntax in
\verb|let|!
\end{itemize}

Keywords might be passed real deep in the code paths.
That’s why CL’s
\verb|declare (ignore ...)| and \verb|\&allow-other-keys| pattern is important.
If one can ignore some keys instead of mandating them, their code can scale gracefully.
And that’s the whole point of design patterns, right?

Update Apr 2024: I now understand and love Clojure map/sequence destructuring.
And Scheme \verb|match| macro.
So this point really goes beyond keywords: it’s about getting the data out of inputs.
Flexibly and within a hand’s reach at all times.

\section*{Concluding} \label{conclusion}

This is not the final list!
You can probably recall some other patterns better than my memory allows me to.
So feel free to reach out via
\href{https://reddit.com/r/lisp/comments/19fh0xx/lisp_design_patterns_overview}{Reddit},
\href{https://news.ycombinator.com/item?id=39140770}{Hacker News comments},
or
\href{mailto:mail@aartaka.me}{email}.

See also:
\href{https://clojurepatterns.com}{Clojure Design Patterns}.
\href{https://nyxt.atlas.engineer/article/lisp-documentation-patterns.org}{And my listing of Lisp documentation patterns} on Nyxt website.

To use the air time productively: I’m searching for new projects/positions!
If your team looks for a CL/Clojure dev with a good knowledge of design patterns (duh), C, and JavaScript,
then we might be a good match!
Shoot me
\href{mailto:mail@aartaka.me}{an email}
sometime 😉


\par\noindent\rule{\textwidth}{0.4pt}
\href{https://creativecommons.org/licenses/by/4.0}{CC-BY 4.0} 2022-2026 by Artyom Bologov (aartaka,)
\href{https://codeberg.org/aartaka/pages/commit/a91befa}{with one commit remixing Claude-generated code}.
Any and all opinions listed here are my own and not representative of my employers; future, past and present.
\end{document}
