The State of Immutability
Immutable data is tightly connected with a functional approach where any mutation is considered as an unwanted side effect. Without further ado, let’s dive into details of mutability and immutability.
You should care, because the trust matters
Using mutable structures is a reason of the trust deficit. Mutation can be done in every code part with an access to the reference.
In one line it can be an object representing a dog, and in the next line the dog can be mutated into a chicken. Such situation enhances fears about state shape and code predictability.
Few words about mutation
The opportunity of mutation provides no guaranty that something will stay unchanged. The worst scenario takes place when some structure is used in separate parts of the application, then any mutation in one component can create a bug in the other one. Such bug is really hard to track because the source of the problem lies out of the scope of the failure. This is an example of a bad side effect.
Mutation is a cause of side effects
Side effect is anything which has impact on the outside of the current scope. If many parts of the code are dealing with the same reference then any change will impact all the parts. Side effects are bad because code starts to be tightly coupled, code fragments instead of following single responsibility rule are doing uncontrolled things outside the scope, they have impact on wider application context . Touching outside is always risky and has unpredictable consequences. When a bug will be found the questions will be risen — Where it was changed? What exactly was changed? Who also have access to the reference? But no history of change is available and questions can’t be easily answered.
Mutation provides only current state
Changes in time related to a single point in memory are not recorded. No access to the previous state. Changing the object attributes is really rewriting the memory, old values are lost. The problem related to lack of the knowledge about changes is growing with number of places having access to the same mutable structure. It stays unknown how the structure is changing in the time.
Mutation is a cause of unpredictable state
If mutation procedure contains many steps, many attributes are being changed then it is fairy possible that procedure can brake in the middle. In such case, the state needs to have rollback to previous valid version, or if not the state will be left in unknown shape. An unknown and unpredictable state can turn into a crash in a place where this structure is consumed.
On the other hand mutation reuse the memory and is fast
Yes that is true, mutation is reusing the memory. Mutation is beneficial for performance and memory usage. But we are in 21-century, memory is cheap and in micro scale of the front-end development it does not matter if some object will be copied or not. No risk that our immutable copies will impact the performance. Garbage collector will clean not used references for us, so no worries about memory leaks or unnecessary memory occupying.
Place Oriented Programming — PLOP
The term presented by Rich Hickey exactly capture the sense of using mutability. If guarantee about value not exists, then our object is only a pointer to the place in the memory, nothing more. Object was created in a shape A, but during the code execution it was transformed into a shape B, C, D and so on. Variables are representing memory not values.
Immutability is about values
Immutability means literally that nothing can be mutated, I can go further — mutation just doesn’t exist. Variable is representing single unchangeable value. No transformation of value because valueA !== valueB. Value is a fact and facts are unchangeable. If something was declared with specific value then rest of the code can fully trust that this exact value is there. This guarantee enables to say that no side effects and no unpredictable state can be observed, most important is return of the trust, and even if some parts of the code needs slightly different structure, they will not touch the original one but work on own versions. Using values provides guarantee and a solid contract. Using memory pointers provides no guarantee and no contract.
Value Oriented Programming — VOP
Contrary to PLOP, VOP is about values only. Code is operating on values not on pointers in the memory. Anything representing different value should be separated structure. The value remains unchanged from its creation until its destruction. Every poor functional language is value oriented.
Any difference should be reflected in the new value
Change doesn’t mean mutation, change means — generate new version of the state which reflects the change. But is it applicable to all cases? This is where some developers, also me, have problems with immutability. The problem starts when exists something big, like a big collection of data, and only small change needs to be done. In such case creating new structure feels wrong, it feels that the memory is used in very non effective way. Why I need to create whole new structure of the data if really only single element needs to be added or one field needs to be changed?
And answer for this question is — because the changed structure is representing a different value. These two collections, the previous and the changed one are not the same. The fact can not be changed, despite from the size of the value, value is unchangeable fact.
So if you start thinking in terms of values, then such doubt should not bother you. If doubt doesn’t go away then Immutable.js comes into rescue, few words about that in the next chapter.
Freeze your objects
Object.freeze method give opportunity to block the object from further mutation.
In above freeze example I’ve tried to mutate frog attributes, but in the result object stays unchanged. Very important is the fact that Object.freeze blocks mutation only in the object fields, in other words it is shallow freeze. Shallow means that any complex structure inside the object can be mutated.
Create new instance instead of doing changes
The spread operator can be used for creating new values from arrays and objects. It is very handy tool, code looks also very clear, it is visible what was changed and what was reused.
Spread operator is doing shallow copy, it means that root object reference is changed but all fields(object) or rows(arrays) are re-used. It also means that this approach is using memory in efficient way.
Use immutable data structures
From my personal point of view using trie data structures can be handy for really complex structures. For anything else I would prefer spread operator.
Is immutability bound to the functional programming
No is not. VOP can be used in other programming paradigms. Nowadays experts and evangelist of Object Oriented Programming also are talking about immutable and stateless objects. Here VOP gives the same benefits as in functional programming. Object without state or object with immutable state gives guarantee and trust for all parties consuming it. Immutable objects are amplifying single responsibility rule because of the need of passing all dependencies into the constructor. Immutability blocks any state change during object live cycle. In other words you are sure that object stays in unchangeable state from beginning to the end.
Forget about setters, and create immutable objects in your OOP. Below some great talk about the subject, it is related to Java language, by Yegor Bugayenko.
Are you ready for immutability?
However the problem is in bad habits, bad habits mostly from object oriented programming. Bad practices are in our minds, under our skin. Bad practices like setters in the object, methods mutating the object state, object as state, huge polymorphic objects and so on, these things should be considered as wrong and never used again.
Change your way of thinking, see the value of value. Choosing immutability is a big step towards predictable, more efficient and less buggy programs.