This course has been updated! We now recommend you take the Functional-Light JavaScript, v3 course.
Transcript from the "Immutability Introduction" Lesson
[00:00:00]
>> Kyle Simpson: Our next discussion is on immutability. Now this word immutability is often taken to be meaning something that we don't mean here. And so I wanna clear up some confusion around what is immutability. What does it mean to the functional programming? When I say the word immutability, I bet at least some of you thought of the const keyword.
[00:00:23] And that's where the misconceptions can get a little bit out of hand. So I wanna talk about this for a minute. If I declare var x = 2, like I'm doing on line 1, and then I say x++,
>> Kyle Simpson: What I'm really doing is not changing the value of 2, the way most people would probably describe.
[00:00:43] What I'm actually doing is reassigning x to a different value. Vars can be reassigned. If I try line 4, I declare a const y, and then I try line 5 to reassign the y. I get an error because reassignment of a constant is not allowed. Now if I were to ask, if I were to pull all of you, tell me what is a constant.
[00:01:12] Many times when I ask that question, the answer I get back is it's a value that doesn't change. From a dictionary definition perspective, that seems reasonable. But from a programming perspective that's not what we mean when we say constant. When we use the word const what we actually mean is nothing to do with the value.
[00:01:31] What we mean is the variable itself can be reassigned.
>> Kyle Simpson: In other words, the const keyword is about assignment mutability or, in this case, assignment immutability.
>> Kyle Simpson: So when a functional programmer talks about immutability, are they really talking about assignment immutability? I'm gonna make the case that assignment immutability, that is what we get with the const keyword, is almost, almost, irrelevant.
[00:02:05] It almost doesn't matter. There is a slight, slight benefit to being able to say, in a variable, this variable cannot be reassigned. I'm not gonna fully dig into this cuz we cover this in another course the [INAUDIBLE] JS foundations. I've also covered in detail and in many of my writings, blogs, and books.
[00:02:25] But assignment mutability is like 1% of the problem, because there's only a small portion of your code that can actually change the variable. That can actually reassign it. If you use the const keyword inside of a block that's ten lines long and you're program is a million lines long, there's only nine other lines that could possibly reassign that variable because const is blocked scope.
[00:02:52] So that other 9,999,000, whatever it is number of code can't effect it. So assignment mutability there is actually not a big deal cuz you can just see the ten lines and know that it doesn't get reassigned. There's only a tiny benefit to saying on line 1, I'm not gonna reassign it on the very next line.
[00:03:12] But if we talk about immutability from the perspective of the value, that's where things are a lot more interesting and a lot more important. So I would argue in a functional programmer insists that we have immutability, they rarely are meaning assignment immutability. What they really mean is value immutability.
[00:03:33] The const keyword doesn't give us value immutability. As you can see when I declare z as a cost and I put an array in it. An array is a mutable value, it can be mutated. When I declare it with const, the only thing that's disallowed is line 8.
[00:03:50] I can't reassign z, but I can absolutely still mutate the value. So if you declare an array, assign it with a const, and then pass that array somewhere, guess what? Somebody can change that array. The fact that you declared it with a const didn't give you any benefit whatsoever, cuz they can mutate the contents of that array.
[00:04:17] Value mutability is a huge problem. When we pass around values by reference, like we do in JavaScript arrays and objects, when we pass those around and anybody's free to mutate whatever they want, that creates havoc. Or more specifically to what we've taught in this course, it lowers your level of confidence as to what any line of code is gonna do.
[00:04:39] When you pass in an array, you don't have very much confidence that somebody's not gonna change it. Does that makes sense? The mutation of a value is a big deal. So it turns out if we'd like to increase our confidence on the value mutation, the const keyword isn't gonna do us any help at all.
[00:04:58] It is the potential to set those landmines where people are gonna be confused, thinking it's making an immutable value when it isn't. So its benefit is dubious at best. I would put it in the negative category but at best, it's neutral. It's not really providing the help that it wants, but there are things that do help.
[00:05:17] That const keyword is not helping here. That const keyword does not really help me that much to tell me that these other six lines in this block aren't gonna reassign. Is the const y really that much more readable than the var x in the very next line? Neither one of them get reassigned.
[00:05:37] Does the const really help that much? It doesn't help that much. And it certainly doesn't help us with z if we were to make z a const, and then pass that array in somewhere. It's not gonna help prevent that array from being mutated. On the other hand, if we do wanna make a value that is immutable, we have a built-in utility as of ES5 called Object.freeze().
[00:06:00] That'll take an array or an object and freeze all of its properties so that they're read-only, they can't be changed. It'll freeze the object so you can't add or remove properties from it. It effectively makes a read-only data structure. It's shallow, it only works on the top level of that object.
[00:06:17] So here, I have a nested array object with 7,8,9, that one is still mutable. But as a nice, quick, and dirty way of making myself a value that is itself immutable so that I can pass it somewhere and have a higher degree of confidence, Object.freeze is a nice, straightforward and fairly performant way of doing that.
[00:06:41]
>> Kyle Simpson: There was a question?
>> Speaker 2: I just have a quick personal question for const, at least, and maybe it'll cover this, but do you think there's some value in using const as a signal for readability? As saying hey, don't mutate this or is that just, I mean-
>> Kyle Simpson: So the question being asked is, does const offer any benefit?
[00:07:05] I was kind of skating over it but we can drill into this point. In this code snippet, is the const keyword actually helping with the readability of this code? Is it actually helping you to know that y is not gonna get reassigned on any of those other six lines of code?
[00:07:23] My point is I don't think it's actually helping that much. Maybe you could squint at it and say, well, it's sort of a little, tiny bit helpful. But it certainly has the precedent, not just in JavaScript, but in every other language that's ever had a const keyword. People have been confused that it creates immutable values when actually it just creates immutable bindings.
[00:07:46] And that confusion is powerful. There are tens of thousands of questions on Stack Overflow where people are frustrated by this const keyword. So there's two things that you could assert about const usage here. One thing that you could assert is that the const is telling a future developer that may maintain or modify this code, don't reassign y.
[00:08:09] That's one argument that I've heard made. I want us to play out that scenario in our head. Let's say you've hired a new junior developer onto the team, and you've told them to put a blue button on the e-commerce page, and they show up with this piece of code.
[00:08:24] And somewhere on line 5 or 6, they changed the value of y, and they run the program and they get an error, and it says sorry, you can't reassign y. What are the odds that that person is gonna say I have to completely rethink how I was gonna do this, because I am not allowed to reassign y, versus going up to line 2 and changing it from const to let or var.
[00:08:50] Which one of those two do you think is a more likely scenario? There's nothing about const that makes it set in stone. You could give the signal please don't do that. But there's lots of other cultural and societal ways that we can communicate intent besides having a keyword that has potential confusion to it.
[00:09:09] You could have put a constant, I mean a comment there that says don't change it. Really, really promise, don't change it. Now, the second thing that is often asserted a lot const is, not that it prevents you from changing it, but that it says I won't change it.
[00:09:26] It tells you what the author's intent was. Don't worry, it's not gonna get reassigned. So, therefore, when you start reading through it, you don't have to worry about why ever being reassigned in that block of code? And that's what I was getting at earlier. Does that really help that much, because there's only a few lines of code where it ever could be reassigned.
[00:09:48] And generally, you can quickly glance at those lines and get the same information. If y does not show up on the left-hand side of an equals, it's not getting reassigned. So I don't know that the const is really actually adding anything. And if it is, it's a very small amount at best.
[00:10:09]
>> Kyle Simpson: That's my take. Others do that calculation differently, but for me, the const keyword is not that helpful. Doesn't mean I don't use it, but I only use it when there's already an immutable value. And I only use it when I'm specifically saying something like const pi equals 3.14.
[00:10:26] Something that's clearly a constant. That's the only time I use that keyword. I do not declare mutable values like arrays with const. I do not make functions with const. I don't do any of that stuff.
>> Kyle Simpson: But the value mutability part is the part I want us to focus on, because that's the part I think that's much more important with functional programming.
[00:10:51] Now, what we're doing with Object.freeze is ensuring that nobody will change it. But is that the important part? Is it really important that we make it so that it would be an error if somebody tried to change it? Similarly to the const keyword. Is that the important part?
[00:11:10] Is it the prevention part that's important? I don't think so. What I think is really important about, for example, using Object.freeze on that array, is that I am signalling to the reader of this code, don't worry, it can't possibly be changed.
>> Kyle Simpson: It's adding to the level of confidence that a reader of this code will have.
[00:11:34] That, I think, is the most important benefit. So when I have an array that I wanna pass into some other function, if I just pass it in as is, the reader of my code has to be worried that somebody may change it. If I Object.freeze it before passing it in, the reader of my code does not have to have that worry.
[00:11:52] It's less for them to be concerned about. Makes the code easier to trust and prove. If you wanna do real, immutable data structures, that is, you need data structures that will be able to not only not be changed, but can be mutated overtime by creating duplicate copies of themselves or at least acting that way.
[00:12:21] You're gonna want a real, immutable data structure and there are libraries that provide these. Facebook has immutable JS. There's another one that's provided called Mori, M-O-R-I. It's written by a guy named David Nolen who's the maintainer of ClojureScript. So he's really into the functional stuff, and he wrote this Mori library.
[00:12:42] There's several others out there as well, so whichever one you like. This was just an illustration here of using immutable JS to create an immutable array, AKA an immutable list. You'll notice that I have sate and newState, because when I create that immutable list and then try to use a new tater method on it, like .set on line 2, it will store that value with the list, but it will not actually mutate the thing that state is pointing at.
[00:13:10] Which is why, on line 4, these two are entirely separate objects, state and newState will be different. Now just on its surface, that might sound like a great thing. But it also might sound like it would be super non-performant to have to clone the array any time we wanna add something to it.
[00:13:27] And this is where the benefit of immutable data structures comes in. Because under the covers, immutable JS is not going to clone the entire array, they're gonna preserve the original list. And any time you make a change, like we did on line 2, all they're gonna do is create a new object that stores only the diff between what it originally was and what it now is.
[00:13:51] Which is why on line, for example, 6 when I ask. I'm sorry on line 9 and line 10 when I ask for state.get 42. In the original one, it isn't present. But in the new one, it is. Because that diff has been transparently under the covers.
>> Kyle Simpson: Represented, but they didn't have to clone the entire array to do that.
[00:14:15] It's actually a little bit like Git, if you know how the Git mechanism works in any way shape or form. When you commit a new thing to a code base, it doesn't recopy up all the files and only copies up what change, so that you can always see the current state of those files.
[00:14:32] That's kind of what's happening here. Under the covers, it only stores the diff between the original list and this new list. And we can always, as we do on line 12, we can always get back out a real array. If we, for some reason, need a real array at some point.
[00:14:49] But storing things in an immutable data structure is one very powerful way of ensuring that your state changes are explicitly tracked, not implicitly tracked. Cuz you can imagine, if you pass an immutable data structure into a function and they try to change it, they'll be getting back a new copy of the data structure, but it won't have affected you at all.
[00:15:12] So you won't be able to mutate it, somebody else won't be able to mutate it. And if you want to mutate it, you'll have to explicitly create newState like we do on line 2.
>> Kyle Simpson: Immutable data structures are very powerful, but they're also kind of a heavyweight solution.
[00:15:39] So I would recommend, if your only goal is to just freeze something, and it's not ever gonna change again, it's just a list of values, you want to pass it in, and you want the reader of your code to know that it's not gonna change. Just call Bbject.freeze cuz that's a very simple fast performing systems.
[00:15:57] It's been around for a really long time since ES5. If you need this full power of tracking changes to a data structure overtime, use an immutable data structure like this one.
>> Kyle Simpson: There's something even more important than what you pass in when you pass in a value, it's when you author a function that can receive a value from somebody else.
[00:16:29]
>> Kyle Simpson: [COUGH] Here I've made a function that ostensibly its purpose is to take in an array and double all the values in the array. If I alter this function this way, I am creating a side effect cuz I am mutating the array that they passed in using the array reference.
[00:16:49] This is a no, no. We do not want to mutate other people's values as a side effect. So if I receive an array, I don't know whether it's been frozen or not. I don't know if it's an immutable data structure or not, but from the perspective of what I offer on this code, I should assume that I'm not allowed to change it regardless of whether I can change it.
[00:17:14] My treatment of somebody else's value is just as important, if not more important, is how they treat my values. My treatment of this list value is incredibly important to creating easier to read and verify code. So I shouldn't author my code that way, I should instead author it as assuming I'm gonna have to create a new list.
[00:17:39]
>> Kyle Simpson: And I'm gonna double each value, but I'm gonna store the value in the new list and not mutate the existing list.
>> Kyle Simpson: You have to take care to make sure that anytime you go to make a change to something, you don't make a change to something in a way that it's going to create a side effect that surprises somebody.
[00:18:02] That's how you increase the readability and verifiability of the code you are working with. To review here immutability. When somebody says that they could be talking about assignment immutability or they could be talking about value immutability. My argument is that value immutability is far more important than assignment immutability.
[00:18:28] That's where you should put your attention. If we're weighting them, it's like 1% the assignment and 99% the value. And the real reason for that is that values are portable. A value passed somewhere can be passed anywhere, because we pass by reference. An assignment, is local, it is not portable.
[00:18:50] Nobody else can affect that assignment other than that immediate code. So just by the virtue of that characteristic, the value mutability is the much bigger concern.