Fairy Tales, and the Implications of Immutability
Tags: javascript, functional, tutorial
Published: 2022-02-22 19:39:53

This is a bit of an aside, less directed to a particular project and more directed to a particular *question*. One of my recent posts, [Decomposing Composition](https://dev.to/parenttobias/decomposing-composition-9m7), led to a someone asking that I perhaps talk about the dangers of the `Array.prototype.reverse` function and others like it:

>One more thing... I know you're not trying to re-create "Composing Software", but would it be worth addressing how Array.reverse() actually modifies the passed array, as well as returning a reference to it? Mutation! Mutation!
>
>That's unlike the other JS functions, which just return something new.
>
>And what implications does that have in a pipeline or composition?

Thanks, Greg! Both the question and what it implies has had me thinking some. And with that...

## The Fairy Tale
The story is well-known: a certain little towheaded kid decides to take a walk in the woods, break into a home, raid the larder, destroy the furniture, and ransack the bedrooms. Upon being caught, she flees the scene.

Yes, I'm referring to *Goldilocks and the Three Bears*. In the story, a family of bears has gone for a walk, to allow their meal time to cool. While out walking, Goldilocks enters their home and samples each ones' food, and chair, and bedding - and in the process consuming or breaking each ones' things.

If you search "What is the moral of Goldilocks and the Three Bears," you might find this:

> The moral of the story is **the need to respect the privacy and property of others and how your actions hurt others**. In conclusion, the story Goldilocks and the three bears illustrate the need to respect others' privacy and property. ...
> *(Taken from [PEDIAA](https://pediaa.com/what-is-the-moral-of-goldilocks-and-the-three-bears/))*

Now, that's **a** moral, and not a bad one, but my suspicion is that the teller of this story was a developer.

## A Different Moral
It seems to me that the story illustrates a very important point: the bears went out, *secure in leaving their stuff in their home*. They trusted in the fact that, when they returned, their stuff would be exactly as they'd left it. Instead, the warning is a dire one: on looking at their possessions again, *things have changed*. In the case of the Baby Bear, things had changed disastrously.

As developers, we can see this as a warning about **immutability**. When we store a value into a variable:

```js
const myName = "Toby";
```

We are doing a few different things here.

1. We are placing a variable, `myName`, into the current execution scope's lookup tables;
2. We are placing a string, `"Toby"`, into memory somewhere;
3. We are "wiring" that variable to that value;
4. By the `const`, we are telling that variable that it cannot be rewired.

So we have two different points to note: 

* First, primitive types are immutable. You can't change them in-place. If we were to `toUpperCase()` that string, we would have a new instance, in a new memory location. The original cannot be changed.
* Next, a `const` can only be declared at initialization. From that point forward, that *reference* is immutable. So not only can't the value be changed, the variable can't be changed.

This implies that we can *trust* that `myName` will remain exactly what we've defined. Any time I call up on `myName`, I will get the same value. In effect, we have placed it in a house *and locked the house*.

Consider a different case:

```js
const myFriends = ['Therese','Daniel','Greg','Annika'];
```

We've done the same here. `myFriends` is a `const` now, so it will **always** point to that same array. All great and fine and wonderful... until we do this:

```js
myFriends.sort((a,b)=>a.localeCompare(b))

console.log(myFriends);
// ['Annika','Daniel','Greg','Therese']
```

So we placed that array into a variable, a `const` no less... but then we [sorted](https://devdocs.io/javascript/global_objects/array/sort) that array. And `Array.prototype.sort` is one of those pesky "in-place" array methods. We have *mutated* the `myFriends` array.

In smaller simpler cases, that may not seem like a big deal. I mean, I *made* that array, and I then changed it - what's wrong with that?

## The Problem
If I can mutate a thing publicly, can I trust that thing? Say we have an admin control panel we're building, and we're storing our data structure like that. That admin control panel might have a number of different controls, but to make it easy to work with, we just let them store their data in arrays, scoped to the `AdminControlPanel` module. They're contained, so they're not contaminating the global namespace.

Imagine further that we designed the thing to be *modular*. Folks could create their own control panel components, and load them in as they like. When they run, they create their data-pools as needed within that `AdminControLPanel`, they do their initialization, and they toddle on about their things.

But what if two of those components used the `Friends` array? Say one of them allows me to add notes about those friends, while another might look up their contact information. Imagine that we load up the `AddNotesAboutFriends` admin module, create the array, and even have a common object style between other things accessing `Friends` that allow for extra details. Works great, loads all the records about our friends, and lets us add, edit and view notes we've made. Great!

Then we have the `ViewFriendDetails` component. Given that we defined a uniform format for those friends, this one might let us search for friends, sort them to make it easier to find, and show a detail view for a selected one. Also works great, no worries.

**But**... what just happened? If our `ViewFriendDetails` component *sorts* that array of `Friends` and the `AddNotesAboutFriends` was looking at that array? We may have broken the *trust* there. We can't rely on the `Friends` array, because something outside of our control is now *mutating* that thing, leaving it in an unexpected and unreliable state!

## Why Immutability Matters
We need, as developers, to work with data. We need to know that, when we look away from it, it won't be like the Weeping Angels in Dr. Who, sneaking about and changing position and doing dangerous stuff. We need, in short, to **trust** that data.

In that article I linked up top, I explored alternative ways to do the `reverseString` lesson common to most programming curricula. One of them was this:

```js
// some utility functions, curried.
const splitOn = (splitString) =>
  (original) =>
    original.split(splitString);
    
const joinWith = (joinString) =>
  (original) =>
    original.join(joinString);

const reverse = (array) => array.reverse();


const reverseString = compose(
  joinWith(''),
  reverse,
  splitOn('')
);
```

Not going to explain it all, I think that last article did pretty well. But some of those small, simple functions are great:

* `splitOn` takes a string to use for our "splitter", and a string to split. From that, it returns an array.
* `joinWith` does the reverse: it takes a string to use as our "joiner", and then joins an array of values into a string.

In both of those cases, as we're transforming that data, we have an entirely new thing. We aren't transforming the string itself *in place*, we are making something new.

Then we have the `reverse` function. I wanted to write a simple wrapper, so I could simply pass in an array and flip it about. Rather than calling `array.reverse()`, I wanted to be able to call `reverse(array)`. But I lost sight of those *implications*. 

"That `reverse` function, in this particular case, really doesn't matter." We only use it on transitional data *anyway*, so the value ends up thrown away. So really, it doesn't matter that `array.reverse()` isn't returning a new thing, right?

## Wrong.
It matters. Why? Because **I can't know the application of my functions**. I have no way of knowing where that `reverse` function may get used down the line. It is a great and useful function, it might pop up all over the place. The whole point of the "Functional Programming" concept is, we can create these small simple one- or two-line functions, and interconnect 'em. And they will work.

But in this case, `array.reverse()` is Goldilocks. We have reached back to the original array reference *and mutated it*. Due to the way javascript passes values, both the original array and the one inside of the function are a *shared reference*. They both view the same memory location, and either can mutate that. This, folks, is a **bad idea**.

## Why?
A key principle in Functional Programming is "purity". When we talk about purity, we mean that our functions should:

* Given the same input, return the same output, and
* Not cause *side effects*.

So, for that `reverse` function, we get the same thing every time: when we pass in an array, the return value is the array reversed. *But we have caused a side effect!* We have mutated the original array, as well as returning it.

We need to be able to trust that, not only does our function do as intended, but that it *doesn't* do anything *unintended*. For example, altering the original array.

## Simple Fixes
In this case, the fix is simple: rather than simply reversing the array, we want to reverse a *copy* of the array:

```js
// bad:
const reverse = (array) => array.reverse();

// better:
const reverse = ([...array]) => array.reverse();
```

In that one, when we are receiving that `array`, we immediately spread it into a *new* array. We no longer refer to the original, so when we `array.reverse()` we are working on our own local copy. And when we return the value, the original array remains untouched.

With that version, regardless of where we use it, when it gets composed into other functions, we are *creating a transformation* rather than *causing a mutation*.

## Other Gotchas
There are other methods and places that we need to be watchful. Here's a typical warning, taken from the [MDN](https://developer.mozilla.org/):

> The **`splice()`** method changes the contents of an array by removing or replacing existing elements and/or adding new elements **[in place](https://en.wikipedia.org/wiki/In-place_algorithm)**. To access part of an array without modifying it, see [`slice()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice).
*(Bolding added for emphasis)*

In the Mozilla docs, if we see an array method marked as **in place**, these are mutators. They will alter the original thing, if we are not careful. They will Goldilocks us, eating our porridge and breaking our chairs.

There are others. The use of objects for storing exposed data can be problematic, as that exposed data can mutate at any time and we have no real way of knowing. We can't trust objects, unless we are very careful and very explicit - they can very easily be mutated.

We can *render* them more trust-worthy, some of which I've written in other articles: rather than using a `class` and creating an exposed object, use a Factory function, and `Object.freeze()` the returned accessor.

## The Point
The moral of the story is, to my mind, **trust**. We need to be able to store our data, and trust that it will be what we expect when we return to it. We can't do that with data that is publicly exposed and in a mutable structure. We need to plan, and we need to be preventative.