Immutability and Safety

Work in clojure for any length of time, and you must get used to the idea that data structures are immutable. For programmers coming from imperative languages this can be jarring, (no loop counters? recursion? wtf?) but after a while, you start to get it, then you start to like it, then you start to rely on it - or at least I have.

To such an extent that it's jarring not to have them. After a recent javascript coding session, I tweeted: "clojure's immutability has forever spoiled me - destructive operations in other langs feel like bugs now."

This prompted Joshua Kerevsky to ask me via email to elaborate, as he has been talking about safety in programming lately. This is a revision of my answer...

Clojure1 is safer (in this sense) because there are never any side-effects when working with data. Languages with side-effects on data (i.e. pretty much every other language I've used) require the programmer to keep a mental model of application state and/or adopt defensive programming styles to avoid bugs caused by them.

The idea is illustrated by these two examples (I used chrome console and the leiningen repl to run them):

javascript:

clojure:

Javascript arrays are mostly (but not always) manipulated via destructive operations such as sort(), while in clojure, the js array's closest analogue (a vector) is never changed by functions that consume it. It's this "mostly" vs "never" distinction that gives rise to a paranoid feeling that I might be breaking things if I forget something in javascript. I also need to learn more "tricks" to get things to work as I expect. To get the javascript version to behave like the clojure one, we must explicitly copy the array e.g. like this:

(bonus: try leaving the var off in front of the concat expression and see how "safe" this version is)

One could argue that it is simply bad form to write javascript and expect it to behave like clojure, but entire books have been written to explain to programmers how to avoid side-effect pitfalls in javascript - and the language is almost unusable without them.

In clojure, there's much less need2 for this kind of "meta language documentation" - and none for protecting data. It's guaranteed not to change. In the example above, the most likely thing to trip up a programmer new to clojure is the need for doall (leave it out and nothing prints since map is lazy - in the repl you'll need to assign the output to see the difference - e.g. (def foo (listFruits fruits)). This is still a bug, but it's one limited to the function in question, not the entire code base.

So my conclusion is that clojure is safer because it has fewer (and much less dangerous) gotchas, the impact of mistakes is limited to the scope of the offending line of code (which will likely be a function or even a let block) and you never3 have to keep a mental model of how state is changing as the instruction pointer advances. It's all right there in front of you.

We all make mistakes, but in clojure, mistakes are limited to the context of the function and never due to implicitly mucking about with application state. This adds confidence when making changes, that is simply not there in languages that cannot make such guarantees.


[1]Clojure is not the only language that features immutability of course - it just happens to be one I use a lot, and like programming in; nor is js alone in having side-effects; i.e. this isn't about championing clojure (or bashing js) it's about immutability, so feel free to substitute your [least] favorite languages as you see fit.

[2] So far at least. Clojure is still young yet, but I don't expect it'll gain this kind of cruft, if for no other reason than because it won't share javascript's experience of being in the front-line of the browser wars.

[3] Wanton use of clojure constructs such as ref, atoms & agents can of course lead to such an environment; however even so, clojure provides well-defined protocols for managing change. If the programmer still creates a state-management hell, that's on the coder - as are most problems in coding; no language can enforce safety, only make it easier or harder.