100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Clojure vs Common Lisp

A practical comparison of Clojure and Common Lisp covering data structures, the JVM host, macros, object systems, and concurrency, for programmers deciding between the two Lisps or moving from one to the other.

PracticeIntermediate10 min readJul 10, 2026
Analogies

Two Lisps, Different Philosophies

Clojure is a Lisp dialect created by Rich Hickey in 2007 that runs on the JVM (and also targets JavaScript via ClojureScript and .NET via ClojureCLR), while Common Lisp is an older, ANSI-standardized language (X3J13, 1994) implemented natively by systems like SBCL, CCL, and ECL. Clojure was designed from the outset around immutable, persistent data structures and functional programming as the default style, whereas Common Lisp is a multi-paradigm language that supports mutable state, CLOS-based object orientation, and functional code equally, leaving the choice entirely to the programmer.

🏏

Cricket analogy: Think of Clojure as a franchise like the IPL, built from scratch in 2008 with T20 rules baked in from day one, while Common Lisp is like Test cricket standardized by the ICC decades earlier, letting each board such as the BCCI or ECB adapt its own ground conventions.

Data Structures and Immutability

Clojure's core data structures — vectors [1 2 3], maps {:a 1}, sets #{1 2 3}, and lists (1 2 3) — are all persistent and immutable by default; every "modification" such as conj or assoc returns a new structure that shares most of its internal tree with the old one via structural sharing, and literal syntax for vectors, maps, and sets is built directly into the reader. Common Lisp's native data structures are lists built from cons cells plus mutable arrays and hash-tables, and while you can write purely functional Common Lisp, mutation via setf, rplaca, and vector-push is idiomatic and common across many real codebases.

🏏

Cricket analogy: Persistent vectors are like a scorecard where each over is appended without erasing history — conj-ing a new ball onto the innings log creates a new complete scorecard sharing all previous overs' data, unlike an old chalk scoreboard rubbed clean and rewritten.

Namespaces and the JVM Host

Clojure organizes code into namespaces declared with ns, and it leans heavily on Java interop — you can call (.toUpperCase "abc") or (System/currentTimeMillis) directly — because it deliberately doesn't reinvent large parts of the standard library that Java and the JVM already provide. Common Lisp uses packages (defpackage, in-package) for namespacing, and interop is implementation-specific, since SBCL, CCL, and ECL each provide their own foreign-function interfaces because Common Lisp has no single canonical host platform the way Clojure has the JVM.

🏏

Cricket analogy: Calling (.toUpperCase "abc") in Clojure is like a domestic player instantly eligible for the national team's full support staff without a separate trial — Clojure interops directly with Java's huge squad of libraries instead of building its own from scratch.

clojure
(ns myapp.core
  (:require [clojure.string :as str])
  (:import (java.util Date)))

(defn shout [s]
  (str/upper-case s)) ;; delegates to Java's String under the hood

(.toUpperCase "abc")        ;; => "ABC" (direct interop)
(System/currentTimeMillis)  ;; => 1751500000000 (static method call)

;; Equivalent Common Lisp package declaration:
;; (defpackage :myapp
;;   (:use :cl)
;;   (:export :shout))
;; (in-package :myapp)

Macros, Syntax, and Object Systems

Both languages share the Lisp macro system built on quote, quasiquote, and unquote — syntax-quote (`) and unquote (~) in Clojure versus backquote and comma in Common Lisp — letting programmers extend the language itself. Common Lisp additionally has CLOS, the Common Lisp Object System, with generic functions, multiple dispatch, and method combinations (:before, :after, :around) built into the standard, whereas Clojure favors protocols (defprotocol), multimethods (defmulti/defmethod), and records (defrecord) — a leaner design that avoids full multiple inheritance in exchange for simplicity.

🏏

Cricket analogy: Clojure's syntax-quote auto-gensym is like a scorer automatically assigning a unique match ID to every fixture to avoid clashing records, while a Common Lisp macro writer must manually stamp each scorecard with gensym to avoid two different matches sharing an ID.

Clojure's syntax-quote (`) automatically generates unique symbol names inside a macro — a form like x# expands to something like x__1234__auto# — to prevent accidental variable capture. Common Lisp macro writers get no such automatic help from backquote and must call gensym by hand whenever a macro introduces a new local binding.

Concurrency and State Management

Clojure ships built-in concurrency primitives — atoms, refs (with software transactional memory via dosync), agents, and core.async channels — engineered specifically because Rich Hickey designed Clojure to make concurrent programming on multicore JVMs tractable without hand-written locking. Common Lisp has no standardized concurrency model in the ANSI spec; each implementation, such as SBCL's native threads or the cross-implementation Bordeaux-threads library, bolts on its own threading facilities, so concurrent code is far less portable across Common Lisp implementations than equivalent code is across Clojure runtimes.

🏏

Cricket analogy: Clojure's atoms and refs are like a scoring app built for multiple scorers updating the same innings total simultaneously without corrupting the count, while Common Lisp offers no standard equivalent — each ground installs its own scoring software with its own concurrency rules.

Don't assume Common Lisp idioms transfer directly when porting code — for example, destructively splicing a list with nconc in translated Common Lisp code can corrupt shared structure in Clojure, since Clojure collections are persistent and assume no one mutates them in place.

  • Clojure runs on the JVM (plus JS and .NET variants); Common Lisp is an ANSI standard implemented natively by SBCL, CCL, ECL, and others.
  • Clojure's core collections are immutable and persistent by default; Common Lisp's native lists, arrays, and hash-tables are mutable by convention.
  • Clojure leans on direct Java interop for its "standard library"; Common Lisp interop is implementation-specific since there's no single host platform.
  • Both share the Lisp macro system, but Clojure's syntax-quote auto-generates hygienic gensyms while Common Lisp macro writers call gensym manually.
  • Common Lisp has CLOS with multiple dispatch and method combinations built in; Clojure uses leaner protocols and multimethods instead.
  • Clojure ships standardized concurrency primitives (atoms, refs, agents, core.async); Common Lisp's ANSI spec defines no concurrency model at all.
  • Porting idiomatic Common Lisp mutation patterns directly into Clojure is a common source of subtle bugs.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ClojureStudyNotes#ClojureVsCommonLisp#Clojure#Common#Lisp#Two#StudyNotes#SkillVeris#ExamPrep