← Back to all guides

Clojure TLDR

A rapid reference guide to the Clojure programming language. A functional Lisp for the JVM with immutable data structures.

v1.12.4 — December 2025

What is Clojure?

Clojure is a modern, dynamic, functional Lisp dialect for the JVM. It emphasizes immutability, functional programming, and seamless Java interoperability.

Functional

First-class functions, immutable data structures, and emphasis on pure functions. Data transformation pipelines.

Lisp Dialect

Code as data (homoiconicity). Powerful macro system for language extension and DSL creation.

JVM-Powered

Access to Java libraries, excellent performance, and deployment anywhere the JVM runs.

Concurrent

Built-in concurrency primitives: atoms, refs (STM), agents. Immutability makes concurrency safer.

Basics

Hello World

;; Print to console
(println "Hello, world!")

;; Return a value
(defn greet [name]
  (str "Hello, " name "!"))

REPL

;; Start REPL
;; $ clj or lein repl

;; Evaluate expressions
user=> (+ 1 2 3)
6

user=> (map inc [1 2 3])
(2 3 4)

Comments

;; Single-line comment

(comment
  ;; This block is ignored but valid code
  (println "Not executed"))

;; Discard next form (reader macro)
#_(println "This is ignored")

Vars & Bindings

;; Define a var (global, dynamic)
(def x 42)

;; Define a function
(defn square [n]
  (* n n))

;; Local bindings
(let [a 10
      b 20
      c (+ a b)]
  c)  ;; 30

;; Private var
(def ^:private secret "hidden")

Data Types

Primitive Types

Type Examples Notes
Numbers 42, 3.14, 22/7, 1N, 1M Long, Double, Ratio, BigInt, BigDecimal
Strings "hello", "multi\nline" Java strings, UTF-16
Characters \a, \newline, \u0041 Java char type
Booleans true, false nil and false are falsy
Nil nil Represents null/nothing
Keywords :name, :user/id, ::local Interned, efficient for keys
Symbols 'foo, 'my-var Identifiers, quoted

Keywords & Symbols

;; Keywords (evaluate to themselves)
:name
:user/email  ;; namespaced keyword
::local       ;; auto-resolved to current ns

;; Keywords as functions (map lookup)
(:name {:name "Alice"})  ;; "Alice"

;; Symbols (identifiers)
'foo
'my-namespace/my-var

Regular Expressions

;; Regex literal
#"\d+"

;; Pattern matching
(re-find #"\d+" "abc123")     ;; "123"
(re-seq #"\w+" "hello world")  ;; ("hello" "world")
(re-matches #"\d+" "123")      ;; "123"

Collections

All core collections are immutable and persistent. Updates return new versions efficiently.

Lists (Linked List)

;; Create (sequential access)
'(1 2 3)
(list 1 2 3)

;; Add to front (O(1))
(cons 0 '(1 2 3))  ;; (0 1 2 3)

;; Access
(first '(1 2 3))   ;; 1
(rest '(1 2 3))    ;; (2 3)
(next '(1 2 3))    ;; (2 3) or nil

Vectors (Indexed)

;; Create (random access O(log32 n) ≈ O(1))
[1 2 3]
(vector 1 2 3)
(vec '(1 2 3))

;; Add to end
(conj [1 2] 3)    ;; [1 2 3]

;; Access by index
(nth [1 2 3] 0)   ;; 1
(get [1 2 3] 1)   ;; 2
([1 2 3] 2)        ;; 3 (vectors are functions)

;; Update
(assoc [1 2 3] 1 42)   ;; [1 42 3]

;; Subvector
(subvec [1 2 3 4] 1 3)  ;; [2 3]

Maps (Hash Map)

;; Create
{:name "Alice" :age 30}
(hash-map :name "Alice" :age 30)

;; Access
(get {:a 1} :a)        ;; 1
(get {:a 1} :b 99)    ;; 99 (default)
({:a 1} :a)            ;; 1 (maps are functions)
(:a {:a 1})            ;; 1 (keywords are functions)

;; Add/update
(assoc {:a 1} :b 2)  ;; {:a 1, :b 2}
(dissoc {:a 1 :b 2} :a)  ;; {:b 2}

;; Merge
(merge {:a 1} {:b 2})  ;; {:a 1, :b 2}

;; Nested update
(assoc-in {:user {:name "Alice"}} [:user :age] 30)
(update-in {:user {:age 30}} [:user :age] inc)

;; Keys and values
(keys {:a 1 :b 2})    ;; (:a :b)
(vals {:a 1 :b 2})    ;; (1 2)

Sets

;; Create
#{1 2 3}
(hash-set 1 2 3)
(set [1 2 3 2 1])  ;; #{1 2 3}

;; Add/remove
(conj #{1 2} 3)     ;; #{1 2 3}
(disj #{1 2 3} 2)  ;; #{1 3}

;; Test membership (sets are functions)
(#{1 2 3} 2)         ;; 2
(contains? #{1 2} 1)  ;; true

;; Set operations
(require '[clojure.set :as set])
(set/union #{1 2} #{2 3})        ;; #{1 2 3}
(set/intersection #{1 2} #{2 3})  ;; #{2}
(set/difference #{1 2} #{2 3})    ;; #{1}

Functions

Defining Functions

;; Named function
(defn greet [name]
  (str "Hello, " name))

;; Multiple arity
(defn greet
  ([] (greet "World"))
  ([name] (str "Hello, " name))
  ([greeting name] (str greeting ", " name)))

;; Variadic (rest args)
(defn sum [& nums]
  (apply + nums))

(sum 1 2 3)  ;; 6

;; With docstring
(defn square
  "Returns the square of n"
  [n]
  (* n n))

Anonymous Functions

;; fn form
(fn [x] (* x x))

;; Short form (reader macro)
#(* % %)
#(+ %1 %2)  ;; % = %1

;; Usage
(map #(* % 2) [1 2 3])  ;; (2 4 6)

;; Partial application
(def add5 (partial + 5))
(add5 10)  ;; 15

;; Composition
(def f (comp str inc))
(f 5)  ;; "6"

Higher-Order Functions

;; map - transform each element
(map inc [1 2 3])  ;; (2 3 4)

;; filter - keep matching elements
(filter even? [1 2 3 4])  ;; (2 4)

;; remove - remove matching elements
(remove even? [1 2 3 4])  ;; (1 3)

;; reduce - accumulate
(reduce + [1 2 3 4])          ;; 10
(reduce + 10 [1 2 3])          ;; 16 (with init)

;; apply - spread collection as args
(apply + [1 2 3])  ;; 6

;; Threading macros
(-> 5 (+ 3) (* 2))       ;; (-> thread-first) 16
(->> [1 2 3]                ;; (->> thread-last)
     (map inc)
     (filter even?))        ;; (2 4)

;; as-> (named threading)
(as-> [1 2 3] $
  (map inc $)
  (filter even? $))        ;; (2 4)

Predicates

;; Type predicates
(string? "hello")  ;; true
(number? 42)       ;; true
(keyword? :foo)    ;; true
(map? {})           ;; true
(vector? [])       ;; true
(set? #{})         ;; true

;; Value predicates
(nil? nil)        ;; true
(empty? [])        ;; true
(even? 4)          ;; true
(odd? 3)           ;; true
(pos? 5)           ;; true
(neg? -1)          ;; true

Destructuring

Extract values from collections in function parameters and let bindings.

Sequential Destructuring

;; Vector/list destructuring
(let [[a b c] [1 2 3]]
  (+ a b c))  ;; 6

;; Rest pattern
(let [[first & rest] [1 2 3 4]]
  [first rest])  ;; [1 (2 3 4)]

;; As (keep original)
(let [[a b :as all] [1 2 3]]
  [a b all])  ;; [1 2 [1 2 3]]

;; Nested
(let [[[a b] c] [[1 2] 3]]
  [a b c])  ;; [1 2 3]

Map Destructuring

;; Basic map destructuring
(let [{name :name age :age} {:name "Alice" :age 30}]
  [name age])  ;; ["Alice" 30]

;; Keys shorthand (when local = keyword)
(let [{:keys [name age]} {:name "Alice" :age 30}]
  [name age])  ;; ["Alice" 30]

;; Strings and symbols
(let [{:strs [name]} {"name" "Bob"}]
  name)

(let [{:syms [x]} {'x 42}]
  x)

;; Default values
(let [{:keys [name age] :or {age 0}} {:name "Alice"}]
  [name age])  ;; ["Alice" 0]

;; As (keep original map)
(let [{:keys [name] :as person} {:name "Alice" :age 30}]
  [name person])

Function Parameters

;; Destructure in parameters
(defn greet [{:keys [name age]}]
  (str "Hello " name ", age " age))

(greet {:name "Alice" :age 30})

;; Sequential destructuring
(defn distance [[x1 y1] [x2 y2]]
  (Math/sqrt (+ (Math/pow (- x2 x1) 2)
                 (Math/pow (- y2 y1) 2))))

Sequences

Sequences are logical lists. All collections can be viewed as sequences.

Sequence Operations

;; Create sequence view
(seq [1 2 3])       ;; (1 2 3)
(seq {:a 1 :b 2})  ;; ([:a 1] [:b 2])

;; Lazy sequences
(range)              ;; (0 1 2 3 ...) infinite!
(range 5)           ;; (0 1 2 3 4)
(range 1 10 2)      ;; (1 3 5 7 9)

(repeat 3 "x")      ;; ("x" "x" "x")
(repeatedly 3 rand) ;; (0.xxx 0.yyy 0.zzz)

(cycle [1 2])        ;; (1 2 1 2 1 2 ...) infinite!

;; Take/drop
(take 3 (range))   ;; (0 1 2)
(drop 2 [1 2 3 4]) ;; (3 4)
(take-while pos? [1 2 0 3])  ;; (1 2)
(drop-while neg? [-1 -2 3])  ;; (3)

Lazy Sequences

;; Lazy operations (don't execute until needed)
(def big-seq (map inc (range 1000000)))  ;; instant!

;; Force realization
(doall (map println [1 2 3]))  ;; realize & retain
(dorun (map println [1 2 3]))  ;; realize & discard

;; Create lazy seq
(defn fib-seq
  ([] (fib-seq 0 1))
  ([a b] (lazy-seq (cons a (fib-seq b (+ a b))))))

(take 10 (fib-seq))  ;; (0 1 1 2 3 5 8 13 21 34)

Transducers

;; Composable transformations (no intermediate seqs)
(def xf
  (comp
    (map inc)
    (filter even?)))

;; Apply to collection
(transduce xf + [1 2 3 4])  ;; 6 (2+4)

;; Into (build collection)
(into [] xf [1 2 3 4])    ;; [2 4]

Macros

Macros transform code at compile time. They receive code as data and return new code.

Common Macros

;; Conditionals
(if condition
  then-expr
  else-expr)

(when condition
  body...)  ;; implicit do, no else

(cond
  (= x 1) "one"
  (= x 2) "two"
  :else "other")

(case x
  1 "one"
  2 "two"
  "other")  ;; constant-time dispatch

(if-let [x (get-value)]
  (use x)
  (default))

(when-let [x (get-value)]
  (use x))

Loops & Iteration

;; doseq - side effects (returns nil)
(doseq [x [1 2 3]]
  (println x))

;; for - list comprehension (lazy)
(for [x [1 2 3]
      y [10 20]]
  (+ x y))  ;; (11 21 12 22 13 23)

;; with :when guard
(for [x (range 10)
      :when (even? x)]
  x)  ;; (0 2 4 6 8)

;; loop/recur - tail recursion
(loop [i 0 acc 0]
  (if (< i 5)
    (recur (inc i) (+ acc i))
    acc))  ;; 10

Defining Macros

;; defmacro
(defmacro unless [condition & body]
  `(if (not ~condition)
     (do ~@body)))

(unless false
  (println "This runs"))

;; Syntax quote ` (namespace-qualify)
;; Unquote ~ (evaluate)
;; Unquote-splicing ~@ (splice sequence)

;; macroexpand - see expanded code
(macroexpand-1 '(unless false (println "hi")))

Java Interop

Seamless access to Java classes, methods, and libraries.

Calling Java

;; Static method
(Math/sqrt 16)                    ;; 4.0
(System/currentTimeMillis)        ;; 1234567890

;; Static field
Math/PI                          ;; 3.141592653589793

;; Constructor
(new java.util.Date)
(java.util.Date.)                 ;; shorter form

;; Instance method
(.toUpperCase "hello")          ;; "HELLO"
(.length "hello")               ;; 5

;; Instance field
(.-x point-object)

;; Chained calls (.. macro)
(.. "hello" (toUpperCase) (substring 0 3))  ;; "HEL"

;; doto - call multiple methods on same object
(doto (new java.util.HashMap)
  (.put "a" 1)
  (.put "b" 2))

Importing & Type Hints

;; Import classes
(import java.util.Date)
(import '[java.util Date Calendar])
(import '[java.io File InputStream])

;; Type hints (avoid reflection)
(defn length [^String s]
  (.length s))

;; Array creation
(make-array Integer/TYPE 10)
(int-array 10)
(into-array [1 2 3])

;; Array access
(aget arr 0)
(aset arr 0 42)

Exceptions

;; Try/catch
(try
  (/ 1 0)
  (catch ArithmeticException e
    (println "Division by zero!"))
  (finally
    (println "Cleanup")))

;; Throw
(throw (Exception. "Error message"))

Concurrency

Clojure provides several reference types for managing state safely in concurrent programs.

Atoms (Synchronous, Independent)

;; Create atom
(def counter (atom 0))

;; Dereference (read)
@counter  ;; 0

;; Update with function (atomic)
(swap! counter inc)         ;; 1
(swap! counter + 10)        ;; 11

;; Reset to value
(reset! counter 0)          ;; 0

;; Compare and set
(compare-and-set! counter 0 100)

Refs (Coordinated, Transactional)

;; Create refs
(def account-a (ref 100))
(def account-b (ref 200))

;; Software Transactional Memory (STM)
(dosync
  (alter account-a - 50)
  (alter account-b + 50))

;; Read in transaction
(dosync
  (+ @account-a @account-b))

;; ref-set (like reset! for atoms)
(dosync
  (ref-set account-a 1000))

Agents (Asynchronous, Independent)

;; Create agent
(def logger (agent []))

;; Send action (async)
(send logger conj "Log entry 1")
(send logger conj "Log entry 2")

;; Read current value
@logger  ;; ["Log entry 1" "Log entry 2"]

;; Wait for all actions to complete
(await logger)

;; send-off for blocking operations
(send-off logger identity)

Futures & Promises

;; Future - run in background
(def f (future
         (Thread/sleep 1000)
         42))

@f  ;; blocks until ready, returns 42

;; Promise - placeholder for value
(def p (promise))

;; Deliver value (once)
(deliver p 42)

@p  ;; 42

;; pmap - parallel map
(pmap slow-function large-collection)

Delays

;; Lazy evaluation, run once
(def d (delay
         (println "Computing...")
         42))

@d  ;; prints "Computing...", returns 42
@d  ;; just returns 42, doesn't recompute

Namespaces & Code Organization

Namespace Declaration

;; At top of file
(ns myapp.core
  "Namespace docstring"
  (:require
    [clojure.string :as str]
    [clojure.set :as set]
    [myapp.util :refer [helper]])
  (:import
    [java.util Date Calendar]))

Require & Use

;; Require (recommended)
(require '[clojure.string :as str])
(str/upper-case "hello")

;; Refer specific vars
(require '[clojure.set :refer [union intersection]])
(union #{1 2} #{2 3})

;; Use (not recommended, pollutes namespace)
(use 'clojure.string)  ;; brings in everything

;; Reload namespace
(require 'myapp.core :reload)

Public & Private

;; Public function (default)
(defn public-fn [] ...)

;; Private function
(defn- private-fn [] ...)
(defn ^:private private-fn [] ...)

Tooling & Project Structure

deps.edn (Clojure CLI)

;; deps.edn
{:paths ["src" "resources"]
 :deps {org.clojure/clojure {:mvn/version "1.12.4"}
        compojure/compojure {:mvn/version "1.7.0"}}
 :aliases
 {:test {:extra-paths ["test"]
          :extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}}}}

Common Commands

# Clojure CLI
clj                    # Start REPL
clj -M -m myapp.core   # Run main function
clj -X:test            # Run with alias

# Leiningen
lein new app myapp     # Create new project
lein repl              # Start REPL
lein run               # Run application
lein test              # Run tests
lein uberjar           # Build standalone JAR

Project Structure

myapp/
├── deps.edn           # Dependencies (CLI)
├── project.clj        # Project config (Lein)
├── src/
│   └── myapp/
│       ├── core.clj
│       └── util.clj
├── test/
│   └── myapp/
│       └── core_test.clj
└── resources/         # Static files

Testing

;; test/myapp/core_test.clj
(ns myapp.core-test
  (:require
    [clojure.test :refer [deftest is testing]]
    [myapp.core :as core]))

(deftest my-test
  (testing "Addition"
    (is (= 4 (+ 2 2))))
  (testing "Strings"
    (is (= "HELLO" (clojure.string/upper-case "hello")))))

;; Run: lein test or clj -X:test
Use clj-kondo for linting, cljfmt for formatting. Most editors have excellent Clojure support via CIDER (Emacs), Calva (VSCode), or Cursive (IntelliJ).