Browse Clojure Foundations for Java Developers

Clojure's Approach to Variable Assignment

Bindings do not change; state changes happen through explicit references like atoms and refs.

In Clojure, a local binding does not change value. That is the default. Instead of reassigning, you compute a new value and bind it (often in a new scope).

1(let [x 1
2      x (inc x)]
3  x)  ;; => 2

That second x is not “mutating x”. It is introducing a new binding that shadows the old one.

Where does state live then?

When you truly need changing state, Clojure makes it explicit: you use references like atoms, refs, or agents (covered later). This keeps most code pure while still letting programs model reality.

Java mental model: locals are effectively final, and state is pushed into dedicated concurrency-safe containers instead of being scattered across objects.

In this section

  • Avoiding Reassignment
    Model work as successive values instead of reassigning locals, and learn why this makes Clojure code easier to review and test.
  • Using `let` for Local Bindings
    Use `let` to name intermediate values, destructure inputs, and keep calculations local without leaking state into the namespace.
  • Managing State with Atoms
    Use atoms when one identity must change over time, and keep the update function pure so state stays explicit and reviewable.
Revised on Friday, April 24, 2026