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 The PurelyFunctional.tv Newsletter. It's a weekly newsletter for
Top comments (7)
i was digging the nice prosemirror module, which implements immutability at the core of an html wysiwyg editor. That module uses two core immutability concepts:
When using it and reading the docs, i was struck by how these two concepts are perfectly implemented as:
The analogy seems to be so deep i didn't yet found the bottom of it.
Even the way we describe the universe uses the concept of immutability :)
I'm not sure if I know enough about the physics you're talking about. I do remember that Haskell's source control system used quantum entanglement math to model the interaction of patches.
Precisely: it follows (well, almost) directly from the non-commutativity of the interaction particles (which propagate state change). It's crazy shit !
Even filling a cabinet could be a function that takes a cabinet and returns a new cabinet, but point taken;)
When we talk about something shared, we also identify something and refer to it. Mutability would mean that we change something without changing it's identity. And immutability would mean that we also change the identity. But how in the real world we know that something is the same or changed? Tastes like chicken - gotta be chicken;) I think in real world, when we expect change we use containers. We give it an identity and expect contents to change, but not the containers itself. That looks like an object with data inside;) But wait a minute! Object is intended to be an instance of a class. It's like having a blueprint (class) and several things (object) built from this blueprint. That looks like we are manufacturing containers to store something, but is this what we are intended to have in the first place? We might have wanted something unique with identity to store our changing data. I think the problem is that data should stay data, and not be a part of an object. I've seen Java objects becoming so big that they look more like a separate subprogram with it's own data and functions. Now imagine this class has a superclass and a child class. How crooked is that?;) One program become a parent of another program, which itself can have children. That escalated quickly.
I gather it would be more reasonable to separate namespaces from classes like Clojure does. Who in sane mind would want to put data in a namespace? That makes no sense, so that data in objects be;)
After clojure.spec it became clear to me how we really reason about data. We state our expectations. It's more verbose than types, but what is type? It's name that is attached to data. Name doesn't tell much, huh? In this respect spec tells more about the data then a type. It's a less strict contract that is closer to what we have in real life and that's amazing.
Rock on! I'm glad you get what I'm talking about.
Great analogy. In fact, one strategy I've heard when modeling a tricky business process is asking the question "How would we do this if we were using papers instead of computers?".
Yes! I love this question!