Clojure atom — Independent State · swap!, reset!, deref | Episode 25
0views
C
CelesteAI
Description
Clojure is an immutable language — and yet you still need state. An atom is how Clojure holds a single value that you can change over time, safely and without locks. In this episode we build two concrete examples with the same primitive: a hit counter (an integer) and a cache (a map).
You'll see `(atom 0)`, `@deref`, `swap!` with a pure function, and `reset!` for blanket replacement — the full atom vocabulary. And because `swap!` is compare-and-set under the hood, your updates are atomic: every read is consistent with every write, even with concurrent callers.
This kicks off Phase 5 — State and Concurrency — the part of Clojure where "immutable" stops meaning "you can't" and starts meaning "you can, safely".
Student code: https://github.com/GoCelesteAI/clojure-for-beginners/tree/main/episode25
Every keystroke is shown on screen with generous pauses so you can follow along at your own pace.
What You'll Learn:
- `(atom 0)` — create a container for one value (any shape works)
- `@counter` / `(deref counter)` — read the current value
- `(swap! counter inc)` — apply a pure fn atomically; the result becomes the new value
- `(swap! cache assoc :k v)` — same primitive, map shape, builds up key by key
- `(swap! counter + 10)` — `swap!` accepts extra args the fn receives
- `(reset! counter 0)` — replace the value unconditionally (use sparingly)
- Compare-and-set — why `swap!`'s fn must be side-effect free
- When to use atoms — independent state that doesn't need to coordinate with other state
Timestamps:
0:00 - Intro
0:15 - Preview: one value, pure fn, atomic update
0:51 - The code — counter atom + cache atom
1:13 - Start the REPL, load the namespace
1:34 - @counter — deref reads the current value (zero)
1:44 - (hit!) — swap! with inc
1:54 - @counter — reads are consistent with writes
2:00 - (swap! counter + 10) — swap! with extra args
2:09 - (reset! counter 0) — blanket replacement
2:21 - @cache — same primitive, map shape
2:27 - (cache-set!) — swap! with assoc
2:45 - (cache-get) — simple get on the deref'd map
3:05 - Control-D
3:20 - clj -M:run — same primitives from main
3:30 - Recap
4:09 - What's next: Episode 26
Key Takeaways:
1. An atom is a container for one value. The value is immutable; the *atom* changes over time.
2. `@atom` reads the current value — cheap, consistent.
3. `(swap! atom f & args)` applies `f` to the current value plus args. The result becomes the new value — atomically.
4. `swap!`'s fn must be pure: Clojure may retry under contention (compare-and-set semantics).
5. `(reset! atom v)` replaces the whole value. Easier to reason about but loses the "evolve from current" property.
6. Same primitive, many shapes: a counter is an atom of `int`; a cache is an atom of `map`; a game's world is an atom of a deeply nested map.
Phase 5 begins. Next up: `ref` and STM — when multiple pieces of state must stay in sync under concurrent updates.
Taught by CelesteAI. Like and subscribe for more Clojure tutorials!