When teaching programming, something that tends to come up a lot is when and how values can change. While there is a lot of jargon (values, references, mutability) to make communicating with people that already understand these ideas easier, it can be difficult to explain to people that don’t already understand the ideas.

The below was initially written to explain to a co-worker learning Clojure what we mean when we say “values are immutable” and I thought maybe someone else could find it useful.


In Clojure, values are immutable by default. If you have (let [foo [1 2 3]] ...) you can’t change that list. You can make foo refer to a new value, but you can’t change the value itself. For example, if you have (let [foo [1 2 3] bar foo] (conj foo 4)) then the values of foo & bar are still the same, [1 2 3] - the conj function returns a new vector (that we haven’t given a name to). Even if you do something like

(let [foo [1 2 3]
       bar foo
       foo (conj foo 4)] ...)

foo now refers to the vector [1 2 3 4], because we’ve “shadowed” what foo refers to, but bar is still [1 2 3].

In languages that have mutable values, for example Python, you could do:

foo = [1, 2, 3]
bar = foo

Then foo would be [1,2,3,4] and so would bar, because the list is mutable, so you can actually change the underlying value the names refer to. In Clojure, even with atoms, only the atom itself is mutable, so, for example:

(def foo (atom [1 2 3]))
(def bar @foo)
(swap! foo conj 4)

Now foo is an atom, @foo is [1 2 3 4] but bar is still [1 2 3]

This makes things much less confusing & reduces the potential for bugs, because values aren’t changing out from under you. In the python example, imagine foo is getting passed to some other function, then that could make the value of your bar variable change!