Immutable Paper
βGrokking Simplicity is by far the gentlest introduction to FP I've encountered and I've been recommending it to people that want an on-ramp to FP ever since.β
Summary: Immutable data appear to contradict our observations of the real world. Things in the world are mutable, so shouldn't our data be mutable, too? It may be counterintuitive, but immutable data does a better job of modeling many of our expectations of the real world.
I've said before that immutable data is easier to reason about because it's more like the real world. That seems counterintuitive because the world is mutable. I can move things around, erase a chalkboard, and even change my name. How, then, is immutable data more like the real world than mutable data?
Here are two stories that will explain my point. Imagine I take a clean sheet of looseleaf paper, the kind with blue lines. I take out a nice, fat marker, and write the word banana in big letters. Then I fold it up and hand it to Susan. "This is very important", I say, "so listen carefully. Put this in your pocket and don't let anyone touch it until you get home. Then you can take it out."
Susan doesn't understand why, but she puts it in her pocket. She makes sure nobody goes in her pocket all day. When she gets home, she finds the paper folded there like she remembers it. She opens it up and reads it.
What will be written on it? (This is not a trick question.)
Everyone will know the answer. It's the word banana. But how did you know? And how are you so sure? In fact, the world fulfills our expectations so many times, all day long, and we don't even think about it. We've learned these tendencies of the universe since we were babies. This kind of reasoning may even be baked into the structure of our nervous systems. Our understanding of the world is intuitive. You might say "we can reason about it".
Now, let me tell you another story. Let's say I construct an object P
(for Paper), and set a property on it to the string "banana"
. I pass
the object to a method on a different object S (for Susan), which stores
that object in a private property. The object S keeps that property
protected, never letting anything write to it, and it makes sure that no
method is ever called that can write to it. After a while, object S
prints out the contents of P.
What do you expect to be stored in P? (This also is not a trick question.)
The answer is "I don't know". It could be "banana"
. It could be
something else if the object P was modified by something else that had a
reference to it. We can't know what it will say because it's mutable and
that lets in magical effects like "action at a distance". We can no
longer reason about it.
Many objects in our universe are constantly changing on their own. The locations of moving cars, the contents of a box of uranium (through decay), the brightness of a candle. Each time we look at it, we expect it to be different. But so, too, are many objects basically static unless acted upon by something else. A house, a glass of water, a piece of paper. The object persists virtually unchanged in any way I would care about. And this is the part of the world that immutable objects model better than mutable objects.
The thing is, once you share two references to the same mutable object, you're way outside the world we live in and know. Two references to the same object would be like two people having the same paper in their pockets at the same time. You could do shared-nothing like Erlang does (everything is copied as it passes between actors), but Erlang also chooses to make things immutable. You could also always copy objects before you store them. This practice requires tough discipline and also undoes any efficiency advantage you gain by using mutable data in the first place.
Mutable objects are important. They can model very common and useful things. For instance, you might model a filing cabinet as a mutable object because you can take stuff out and put stuff in. But you will want to model the papers inside as immutable because they can't change while they're inside. Having both is what makes it work.
Clojure makes it very easy to separate the two. Data structures like lists, vectors, hash maps, and sets are all immutable in Clojure. And Clojure has a toolbox of mutable things, too: atoms, refs, agents, and vars. These are good for modeling the current state of something shared and changing.
Well, I hope these stories explain why immutable objects do act like real world objects in many useful cases, and how this is a key part of why people claim that Clojure code is easier to reason about. If you like this idea and want to explore it more, check out PurelyFunctional.tv Online Mentoring. It's step-by-step guidance from Clojure dabbler to professional. I answer your questions in video lessons using real-world language, metaphors, and exercises.