Functional JavaScript First Steps, v2

Immutable Data

Anjana Vakil
Software Engineer & Educator
Functional JavaScript First Steps, v2

Lesson Description

The "Immutable Data" Lesson is part of the full, Functional JavaScript First Steps, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Anjana introduces immutability and explains why avoiding mutation is a core principle of functional programming. With immutable data, when something needs to be updated, a new copy is returned rather than changing the original object. This eliminates the need for state in the function.

Preview
Close

Transcript from the "Immutable Data" Lesson

[00:00:00]
>> Anjana Vakil: In addition to functional purity or sort of a consequence, again, of our wanting to do programming deterministically and with minimal cases where the thing changed out from underneath me in a way I didn't expect, we wanna avoid mutating data. So when I say mutating data, I don't just mean getting it bitten by a radioactive spider and giving it superpowers, I mean changing the data in place, we say, or sort of the same entity, like an array, for example, now has different values inside of it.

[00:00:46]
So that's what we call changing data in-place and mutability and in immutable land, we don't want to do that. We never wanna, let's say we take in an array, we never wanna mess with that original array that we took in. Instead of changing it in-place, we want to replace it with an updated new version of the array without messing with the original version.

[00:01:21]
>> Anjana Vakil: So, let's take a very simple array as an example. We've got a bunch of cities in an array that are cities in India that those names are super outdated and colonial and we don't like them anymore, some of them. So for example, Bombay, which is what the city, for example, my dad grew up in, which was called Bombay when I was a kid, is no longer called Bombay, it is called Mumbai.

[00:01:52]
So we need to update this data. And in a mutating way, we could update it by assigning a new value, Mumbai, to the element at index one in the array, right? And we're used to doing this in our JavaScript code, hopefully reasonably often. And so now cities is different.

[00:02:14]
Before cities had Bombay in it, now it does not. It has Mumbai instead. Cities itself, same array. As far as JavaScript is concerned, it's the same thing that it's pointing to in its memory. Okay, the problem with that is, okay, maybe we won't get into it in this particular case, but if an array or any kind of value changes out from under me that means, again, we have state, we have a value that is changing over time, a single value cities is now talking about different data.

[00:02:57]
So if I were to run a function that used cities at two different points, one before the update and one after now I am having the pain of stateful headaches [LAUGH] in my programming. So instead, what we wanna do, is not update the old object, in this case, an array, but rather create a new one that has the correct data or the updated data.

[00:03:33]
So one way we could do that here is by using, in this case, we can use the built in map. It's pretty pure. It returns a new array. It doesn't mutate the array, but we could also use our helper map function. We're going to take each thing in the array of the original old cities and then decide if we need to update it or not, essentially.

[00:03:56]
And if we do, we will return the corresponding new value. So in this case we're saying, okay, Delhi? Yep, that's still Delhi, continue. Bombay? Nope, new name, Mumbai. Bangalore? Nope, new name, Bengaluru. So what we return then, so this is the inner function here, this a transformer function that we pass in to our higher order map, is then just gonna take in the old data and return the new data.

[00:04:35]
And map is gonna take care of doing that for everything in the array and then returning a new array with that transformation. Crucially, old cities never changes. So if my coworker was working with the old cities array and their tests would fail if I swapped out all the values, they're still good.

[00:04:59]
Old cities is still old cities. New cities is new data that I can work with that is based on that old city data. All right.
>> Anjana Vakil: No, you might be wondering, okay, that's fine if we have three things in an array, but every time we use a map or a filter or reduce, we're turning a new array.

[00:05:21]
That means we have to create more space for more data, right? We're keeping the old data around and adding the new data to our program somewhere, so it has to remember all of that stuff in memory. We're gonna start taking up a bunch of space, [LAUGH]. And it might also become pretty inefficient if we are dealing with very complex data.

[00:05:45]
And so there can start to be similarly with recursion, but we started to run into some performance problems once we get larger and larger inputs. There can also be some issues to this whole copy instead of mutate strategy like make a new modified copy instead of changing the original.

[00:06:08]
>> Anjana Vakil: And that can become pretty inefficient in both time and space concerns. And so there is a strategy or a entity, I guess, in a lot of functional programming, languages and toolkits and things like that that helps us deal with that problem. And you'll sometimes hear them called immutable data structures.

[00:06:34]
And this is essentially a special type of array in this case, but you could also imagine an object or what have you that we can create copies of that are a little bit more efficient than just copying every single thing in the array all the time. So you might hear them called immutable data structures.

[00:06:53]
You might also sometimes hear them called persistent data structures, they are a really, I think, really cool, fascinating implementation detail, essentially, that we're not gonna spend too much time on. But wanna think about it a little bit, that is a kind of very clever way of helping us, instead of allocating if we have an array with a bunch of elements and we need to update it, we would have to allocate a whole new array of that same old length if we're working with regular data structures.

[00:07:25]
If we're working with immutable data structures, they have special optimizations baked in that help us only copy the changing parts. So essentially, what persistent data structures do is they, on the surface, look like an array. But under the hood, they're actually trees that take, okay, let's say we had an array of zoo animals that started with a monkey and then there was a bunny, and then there was a panda and a bear and a blah, blah, blah.

[00:08:00]
And now it's, a UFO just landed and squashed the bunny. But now we have a new animal on the planet, it's an alien. And so if I wanted to create an updated array with just one value changed, like bunny becomes alien. I don't wanna have to make new copies of all the same stuff that didn't change.

[00:08:28]
So what persistent data structures let us do is, if I had my original array on call, it was called zoo, then when I create my updated array, which we'll call new, I only need to make a copy of the values, and in this case, it clumps some of the values together cuz doing on each individual value isn't always so efficient.

[00:08:50]
So it might make, let's say in this case, it's chunking them into twos. We update that node in this tree with the new value. And in this case, yeah, we had a little bit of copying, we copied the monkey. And then,
>> Anjana Vakil: The data structure points to the rest of the same old data that we already had in memory without having to create a new copy of it.

[00:09:23]
So essentially, and there's a lot more that could be said about this, and I also have some links in the further reading that you can dig into. But essentially, what we're doing is we're leveraging some very clever trickery under the hood to make it much faster when I want to copy that new array that changes just one thing.

[00:09:44]
I no longer have to allocate new memory and spend all the time allocating that memory for each thing in the original array that didn't change. I get to skip all the unchanged parts and just update what I need to. This is something that is baked into some functional programming languages, JavaScript is not one of them.

[00:10:06]
In JavaScript, we don't have this kind of functionality out of the box. Some languages like Clojure, for example, have these built in. But what luckily for us, we have, as I said, a massive ecosystem of other people who want to do functional programming in JavaScript, which means they have implemented these kind of things for us, and we can take advantage of that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Start a 7-Day Free Trial