The State of Immutability

Maciej Sikora
DailyJS
Published in
8 min readDec 30, 2017

Immutability is a hot subject in modern JavaScript. The reason why this topic is so popular now is of course the functional programming paradigm.

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.

About immutable data structures in JavaScript

In JavaScript, and many different languages, an assign statement works in two ways. For primary types (string, number, bool) assignment is passing the value, for complex types (objects) it is passing the reference(memory pointer). For complex types, any change will impact all occurrences because they really are only pointers to the same place. In the effect only primary types are immutable and can be safely changed, everything beyond primary types is mutable by default.

That said, let’s think what is the point in being immutable at all. JavaScript doesn’t have this immutability out of the box, there are no immutable structures for composites, and even no future plans for introducing that into the language. Why should you care, if the language doesn’t care?

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 the access to the reference.

JavaScript is very flexible, it means that any object can be transformed on the fly into something really different.

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, of course those unwanted ones, because code starts to be tightly coupled, code fragments instead of following single responsibility rule, are doing uncontrolled things outside their 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

When one reference is being many times changed, there is no history of these changes. 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.

Transformation car object into bike object— no parking spot monitoring!

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.

…To be clear I am referring to the front-end side problems, it is understandable that there are many other kind of software, where the memory matters the most.

We have plenty of memory

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 rebuilding 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.

Immutable structure vs Mutable structure

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 pure 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 were 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 the 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 the 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.

Value Oriented Programming in JavaScript

VOP can be implemented in JavaScript. As said before JavaScript has no immutable structures, but immutability can be achieved by principles and rules.

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 still 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

JavaScript has no immutable structures out of box but there are third party implementation of such. The most popular are Immutable.js and mori. Both are base on trie data structure which is kind of search tree structure. Such structures allows for very efficient way of creating new instances without copying.

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. And until you will see performance issues, I would not really consider any third party solutions.

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?

All things considered — immutability can be achieved by rules and principles. JavaScript doesn’t have immutability builded in, spread operator and external libs can easily help in implementing it though. If immutability is about rules, then everything is in the developer hands. If you see the value of value, then from this moment, the mutation should be gone in your application.

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.

--

--