Use atoms when one identity must change over time, and keep the update function pure so state stays explicit and reviewable.
An atom is what you use after local bindings stop being enough.
That is the simplest way to connect this page to the rest of the chapter:
let is for local names during one calculationatom is for one identity whose value changes over timeIf you keep those jobs separate, Clojure state management becomes much easier to reason about.
Officially, atoms are for shared, synchronous, independent state. In practical Java-to-Clojure terms, that means:
| Good fit | Why an atom works |
|---|---|
| In-memory cache | One current value changes as requests arrive |
| UI or session state | One identity advances from one immutable value to the next |
| Counters and small registries | Updates are independent and should happen now |
An atom is not a substitute for:
letThose distinctions matter because many migration problems come from choosing an atom for every changing thing.
Start with a pure transition function:
1(defn reserve-seat [show seat-id]
2 (update show :reserved-seats conj seat-id))
Then let the atom own the changing identity:
1(def current-show
2 (atom {:show/id "evening-1"
3 :reserved-seats #{}}))
4
5(swap! current-show reserve-seat "A-12")
6@current-show
This split is the important design move:
That is a much stronger model than “I need a mutable variable.”
swap! Wants A Pure FunctionThe official atom reference makes one rule especially important: the function passed to swap! may run more than once because the update can retry under contention.
That means this is bad:
1(swap! current-show
2 (fn [show]
3 (println "reserving seat") ;; bad: side effect inside retryable update
4 (reserve-seat show "A-12")))
And this is better:
1(println "reserving seat")
2(swap! current-show reserve-seat "A-12")
The atom update stays pure, and the side effect is no longer tied to retry behavior.
reset! Versus swap!Use the right update tool for the job:
| Function | Use it when |
|---|---|
swap! |
The next value should be derived from the current value |
reset! |
You already know the exact new value to install |
Examples:
1(swap! current-show update :reserved-seats conj "A-13")
2
3(reset! current-show
4 {:show/id "late-show"
5 :reserved-seats #{}})
Most business-state updates should bias toward swap! because they express a transition from the current value.
The closest familiar concept is often AtomicReference<T>, but there is an important Clojure difference:
So the atomic part controls identity change, while the data inside still behaves like ordinary immutable values.
That combination is what keeps the code readable under concurrency.
If you only need a temporary name during one function call, use let.
If you only need a result from a pure transformation, return the value.
Reach for an atom only when the program truly has one current value that must change over time and be seen consistently by callers.
That is the main discipline this page is trying to teach.