Immutability as a secret test of character
Despite the fact that there is so much awe and worship of immutability in JS’s FP’s part of community, my beginnings with the concept were not at all related to functional programming.
It started with ESLint and no-param-reassign rule which guards against redeclaring a param. Those were my first steps with ESLint and I went with very strict approach in order to observe in the process which of them I really need. If I were filtering them out or found annoying, they would have to go. (More than six months later I dropped a number of them; some due to co-programmers not using ESLint.) No-param-reassign highlighted nicely and soon after I started using it, I met just one situation that convinced me that it’s the right way.
So, picture this,
Whatever this function was meant to do is of no relevance here. The point is
array is lost after first loop. Sure, a number of improvements could be done here (i.e., checking condition first, or creating separate array for new values) but basically something like that, just more imperatively elaborate, happened to me. I needed to use original array again but it was forever gone. And that’s when I knew.
After immersing myself — which is the only way to do anything — in the world of functional programming I discovered that what I learned by myself has a name. It’s immutability. It means you do not mutate variables. You don’t change their value, you don’t reassign them. They are sacred.
(Being non-English native speaker, I still connect word mutation with “Teenage Mutant Ninja Turtles,” so whole subject has like +5 to coolness for me. But that’s just by the way.)
But who’s to say that one can’t write in this manner? ESLint already helps with params, and outsourcing as much as possible to pure functions which should be as concise as possible makes it easier to control variables. Once you set it, leave it. It’s that simple.
And thus the title of this story, as immutability in JS is possible but requires discipline, cautiousness, and character.
Let’s run some examples now.
The default of primitive
A question of default value:
safeGuardES5 is a common way to run with it. The workaround for the sake of workaround — “For sport,” as we would say in my native language — would be
safeGuardES5immutable which leaves param passed to function untouched. Mind you, at this point we’re actually doing it for sport, exploring some ideas to see where they’ll take us. The practical use is up the the readers.
safeGuardES6 function which uses native default value which does not produce extra variable but in case we wanted the original one (being
undefined but still), we wouldn’t have it. Though, I don’t think it happened to me, hence widely acceptable practice of overwriting undefined parameter. Acceptable but mutating.
For more than two params I use object and that allows me to do this:
The tricky arrays
Primitives got covered. But what with arrays? Let’s start with with real life example:
Section before shows code I found and after the code I left. Now, this is quite interesting situation here. Let’s follow
If there is no
startAt, return array. Otherwise,
- Cut out piece of array from
startAtto the end of array.
splicereturns the piece that was cut out.
- Concat this piece with the rest of array.
Works but only for the first time, the second time
concat which leave original object intact.
Splice can be very dangerous because it mutates object and returns part that was taken while leaving original irreversibly crippled. But this is a tale for another story.)
And this is where immutability becomes something more than just stylistic matter as it was mostly with default value. With our original function we were mutating object (array) that was used in other methods as well.
ES6 brought us
const which prevents variable (now constant) from reassigning and primitives are by design immutable but what with objects? Example above with Array showed the danger of it.
Array has much more methods that would allow us to control a situation. With
array.slice(0) we can create a shallow copy of
array. And object? There is
Object.assign() but again, as all objects in JS, it’s tricky:
In other words, watch your fingers.
We can freeze object but again, this affects only first level. Like that:
Generally, working with objects is more complicated and I tend to avoid actions other than creating them and reading from them, and that might be the best approach.