How I learned to stop worrying and love mutation
Chris Eidhof at Playgrounds Conference, 2017
It's still there, European background. We other side of the equator. Before I start, I just want to take a moment to thank Andyy. Before you start clapping, I've been on the side of organizing a conference. I know how much work it is. And not only how much work, but how much stress it is. He has to please so many people, and there's so many things you have to think of, things you don't even think of if you're attending or presenting. It's so much, just months of stress and hard work. He just did a fantastic job. I don't know. I see happy faces all over the audience, and then all the speakers. I've been talking to so many great people and just let's give a really big hand for Andyy wherever he is. So awesome. Make sure to thank him later on. Let's get started.
My talk is about mutation. Here's some nice immutable stuff. When Swift came out, it was sort of branded as “Objective-C without the C” at first. It's a great way to do object oriented programming. People came at it from the object oriented programming world, but there's also other people. I came at it from the functional side. I saw it as a great way to do functional programming. There's all these different influences, and I wouldn't say that Swift is an OO language or a functional language; it's sort of its own thing with its own patterns and things.
One thing that the functional programmers say is that immutability is really good. It's sort of a dogma. Just like OO programmers have their dogmas, functional programmers have them as well. One of the things they say is immutability is the best, and they want to make everything immutable. In a way, I really agree with that sentiment. There's something really good about it. Yet in Swift, it's a little bit more subtle and that's what I want to try to show today.
We have this string here, and it's defined using let. If you define a let variable, and you have the Struct inside that variable, that means that you can never mutate it again. This string is immutable. We cannot change it. This is really great because we can read through our program, even though it's one line. We can read through our program and see that it never changes. If we actually try to change, so if we say like String to append, and try to append an exclamation point, we get an error. This is really nice. It's sort of this check for us that this doesn't change ever.
We can go into this, fix it. You can probably not read it, but it suggests to make this string into a var. We can do that, and now we can append to this string. If you're a functional programmer you might really dislike this because now we use mutability and mutability is bad. We'll go into that. Yeah, so this is a bad thing so what do we do? Well, if we back up a little bit, another thing we could do and sort of the functional way, the right way, is to say let other string equal string plus exclamation point. Now, what we have achieved is we have this new variable, other string, and we still have our original string available and it hasn't changed. We can verify that. It's also very easy to see.
Here you see the original string is still there without the exclamation point. That's great. If you really want to do it sort of like the Swift way, you might also even make a method for this. I'm going to overly complicate things, so let's do that. Write and extension of String, and just like we had an Objective-C, we're going to write a method appending, an appending some other string. This is going to return a new string, so it's not going to append to the original string, it's going to return a new string. We just return self plus other. Now we could say something like string that appending and append an exclamation point. It's just a really elaborate way to add an exclamation point. But the nice thing is that we didn't use any mutability, so the functional programmers will be really happy.
To look at why mutability is considered bad, there are many reasons. I could talk about this for two hours. I'm just going to tell about two examples. One example, and one reason why mutability is considered bad is because it becomes hard to read. If you create a var, then as you go through the lines of your program you have to at every line consider what's the current value. That's more complicated to read sometimes. The other thing is what Wendy showed yesterday. I'm going to steal her example a little bit. Let's make a new page. What Wendy did is she say, "Well, we have this class Pin," and I'll just create a property just to quickly mock it. Let's say the Pin has a title or something.
If we create a p1, which is a Pin and now we created p2, which is the same as p1. Now, we have a problem potentially. If we say something like p1 the `title = test`, and then we print p2 to title, it also changed. Here it's very easy to see. We could just create this p1 and it's like four lines, or five if you include the blank line, it's very easy to see what's going on. But if you consider what usually happens, is you have different view controllers and maybe they both point to the same object and then it becomes much harder to understand which view controller is changing and do I need to subscribe?
If we back up a little bit and try to understand what's going on here, it may be a little bit more obvious. When we write let p1, we create a new variable, and this is going to hold a Pin. But it's actually a little bit more subtle. It's going to hold a reference to a Pin, so a reference to an instance of this class. We wrote our variable using let. That means it's constant, but what's constant is not the class or the object, it's the reference. We always refer to the same objects, even though that object might change. On the next line when we create p2, we also create a constant reference, but it's only a reference and it points to the same object as p1.
If one of the two changes the object, they both get a new value. That's why these things are very deeply connected. If you change p1, you change p2. Even if they're in different view controllers or in way completely remote parts of your app, you still have that same effect, so it is spooky action at a distance. This is a good thing. It makes a lot of things possible, and it's a nice pattern that we use, but it's also problematic. It becomes even more problematic if you're doing multi-thread programming.
Let's say you're doing something like this, like you say if p1, that title is empty then do one branch and otherwise do another branch. Now if you do some very important logic on line nine where you're checking that the title is empty, if there's another thread that holds a reference to that same object, you might get that thread kick in. Let's say you're doing something like p1 the `title = “hi”`. Now what might happen is in between this check for the condition and setting the title to “hi”, another thread might kick in, modify the object and all your assumptions are gone. This is really hard, and it's really hard to get this right. That's why if you have immutable objects, so if you have objects that never change you don't have this problem. That's one of the reasons why functional programmers like immutable values so much.
Let's create an immutable value. Create yet another page. Okay, here we go. I'll create a Struct because structs are going to solve everything. We're going to create a Person Struct and we'll add a name property, a string. To make things a little bit more complicated, we'll also create an address Struct and this has a city, which is a string. We're going to add that address to the person, so address is an address. Now we can create values of this Struct. We could do something like let Chris = Person. Chris and then that's the address. We just provide Berlin as the city. Now we have an immutable value. If we say `chris.name = “Justin”`, for example it's not going to work.
This is a really cool thing about structs. If you have a struct and you define a variable using let and put a struct in that, and that struct consists out of other structs, it means that it's going to never change. You know just that the value will always be the same. That's a really cool thing. If you're reading through code, you just know this is never going to change. That's a really good thing. Let me quickly restart Xcode because it's not actually showing the error. I've practiced this so I know it's going to show the error if I restart. Let's see. It's going to show a fix-it, which I want to use. There we go. Okay. The key to live-coding is just practicing a lot. I hope I practiced enough.
It's going to show the fix-it and it tells us to make this thing into a var. It does that for us. Now we can change the name. If there's one thing, only one thing you can take away from this presentation, it's that the mutability of a struct is controlled through the var and let keyword. If you have a let struct, it's immutable. If you have a var struct, it's mutable. Only when we make it a var, we get a mutable struct, or a mutable variable actually. Okay, so we can do this but how could we do it more functionally?
How could we do something like let Chris and then change it? It's actually not so hard; it's just a lot of work. We can write an extension on Person and let's say we want to write a method to move to a different city. We'll call it moved to city and provide a string. Instead of changing the Person, we're going to return a new person. This is sort of the functional way. You return a Person and then you copy all the properties. You say solve the name, or actually we can just write name. For address, we need to provide the city. This is the new city. Okay.
Now we can use this and we can say something like `let otherChris = chris` that moved to ... Let's say I'm going to move to Melbourne. Hey, the most liveable city, right? This works and now we're nice and functional and everything is let. Everything is really great. But I don't really like it because now this is clearly very simple code. Like if you look at the struct it only has two properties, but if you add 10 properties you have to type a lot. You have to do all this copying and it's just not very nice. Let's see how we can do this in a simpler way.
What if we write a function move(to city:) and we actually change the struct. So we do something like `self.address.city = city`. Now the compiler is going to complain. It says, "Hey, you cannot do this." The reason is because it doesn't really know what kind of variable you're working with. How do you know that you can actually change this struct? If we do the fix-it it tells us, well self is immutable. You have to mark the method as mutating so we can do that. Nice. Now let's try to use our move method. We can chris.move to “Melbourne” and this should change the struct in place. But here we have our first problem. We defined our struct using let and we made this promise that let structs are never going to change. The compiler again helps us and it tells us to make it into a var.
Now we have a var variable and now we can actually change it. What this mutating keyword does, it tells you that self is mutable. If we think about what self is, in this case it's really just this Chris variable. Mutating directly corresponds to this self variable and the chris variable. We can actually leave off the self., that's basically unnecessary. The cool thing is that the compiler enforces this. We saw that if we use a let, we kind of call it mutating method. We can also not leave it off, like as we saw before, the compiler will tell us to add it. You can actually use this really handy shortcut. If you do control+option+command+F, you do fix all in scope, and it fixes all the errors for you. Really useful.
Okay, so we have our move method and we have our moved methods so we can do either one. Wouldn't it be nice if we had an easier way to implement this moved method? It turns out that you can use this one pattern to create an immutable method out of a mutable method. It's only three lines. I'll delete this. The three lines are like this. So first you create a copy of self and in the end you return that copy. Now, we actually change and call it mutating method, so we can say copy.move to city. You can create an immutable version of every mutable method that you have in a struct with these three lines.
This is really essential to the way that structs work. If you have a struct variable, then there's no reference inside the variable. It actually contains that struct value, that's how you can think of it in your head, even though it's maybe implemented sometimes in a different way. It is variable, sort of a box for that value. The value is in there and that's why if you create a new variable, if for example, say var copy, it now contains the value of self. It has nothing to do with self anymore. It's now an independent variable with an independent value. We can adjust that and we are not changing self in the process. Let's see if this works. If we say let chris, and now we say `let otherChris = chris` that moved to “Melbourne”, and now we can dump the two. Let's dump chris and let's dump otherChris. This will give us some console output.
Here we go. We first have the original value, which is still in Berlin, and then we have the new value, which is in Melbourne. It really created an independent copy and mutated that. I think it's a nice technique. We can always go from a mutable method to an immutable method. Even if we only use mutable method ... Let's make this a var again. If at some point we want to create an independent immutable method, or copy of the struct, if we want to create an independent immutable copy we can just do that. We just say `let otherChris = chris`, for example. Now nobody's going to change this other variable anymore. It's immutable.
Structs give you this flexibility and you just control the mutability through your variables. If you make a var, you have a mutable variable. If you have a let, you have an immutable variable. You can really defer this to the last moment and use whichever you want. Let's use this knowledge of creating an immutable version to improve Array a little bit. Let's create another page, and let's say we have an array 1, 2, 3. Now, if we want to have one of these appending methods, we can now do that. We can write an extension under Array, and we can say appending and then some element. Instead of mutating it's going to return a new array. Elements.
The way we do this, the easiest way would be to write return self plus element, but we can also use a recipe that we had before. We say `var copy = self`. Now we have an independent copy. We return that copy and now in here we can call it mutating method. We can say copy that appends, append element. Now what's really cool about the immutable style is that we can say array, that appending, append 4, and then the map, and then square it, and then I don't know, the filter. This is just random typing. You can really have this nice functional chain. That's, I think, something that's really cool about the functional style is you can change things with the dot operator.
The nice thing about mutability is that you can mutate things in place. You can be way more efficient in maybe space and time, whereas with the functional style you can maybe be more efficient, or write more readable codes depending on your definition of readability. We now know how to go from a mutable method to an immutable version. Can we also go the other way around? We can. Sometimes this is very useful. I sometimes teach workshops, and one of the exercises we have in there is a remove where method. You remove where some condition holds. A condition is just a function that checks element. The challenge is to implement this as a mutating method.
If you're actually going to mutate it in place and work with indices, it's not so easy to write it correctly the first time around because you need to do some manipulation and then incrementing depending on conditions. But there's actually a quick hack and a quick way to write this. That's just by using filter. If we write filter, and say we want to filter out all the elements for which a condition does not hold, so does not. The output of this filter is an array. What's really cool is we can just assign that to self. If you're used to object oriented programming you might think, "Ugh, assigning to self? What is this?" But with structs, you have to think of the self as the variable that you're actually mutating. Remember that this mutating directly corresponds to the variable and self is that variable.
If we stick this into an array, let's say x. We're doing functional programming, so we can write x as a variable name. Now, we can write x to remove. If you sort of in your head try to inline this remove method, so let's remove everything that's less than 5. If you're trying to inline, you're basically saying `x = filter`. We can only do this because x is a var. If it would be a let, you cannot call the remove method. This is very useful. Yesterday, as I was watching Soroush’s talk, I realized that you could also use this to write a pop method on linked lists. Let's try to do that.
I'll rename it so I can share playground later. Soroush had this linked list. He defined it in a much nicer way probably, but I'll have an empty case and then a case word note, which has an A and then the rest of the linked list, so the tail basically. You could write LinkedList<A>. One cool thing is if you write these recursive ... Oh, I wrote extension. Sorry, this should be enum linked list. One cool thing is if you write these recursive definitions you can also even leave off the generic parameter and it works. You can also mark individual cases that's indirect.
Here we have our linked lists definition. I actually do prefer it like this. It's a bit clearer. Now we can create a linked list. Let's write a list variable, which is a LinkedList. We want to have a note with one and then another note with two, and then empty. It already inferred type for us, so this is a linked list of integer. What we will call this, write a pop method, that sort of pops off the linked list. How do we do that? Well, we write an extension on linked list, and write a mutating method pop(). This is going to return an optional A. Again, I'm just stealing Soroush’s codes, so just switch on self. If we have an empty list, well we cannot really pop anything. We just immediately return nil. If we have an unempty list, so if we have a node, we have some value and then a remainder, we have to add the let to bind the variables.
Now while we want to return the value, that's clear, and what's really cool is in between ... wait till I scroll ... in between we can reassign self to the remainder. Again, that might feel like we're changing the value or something, but we're really changing the variable. The linked list, the value stays the same. We're only changing the variable, the contents of the variable. Let's see if this works. We can make a copy of list. We're dealing with value type, so this copy works just exactly like with structs. We've defined it as var so that we can pop up, so we can say copy.pop and we get one, copy.pop, we get two, pop, we get nil. Awesome. Yeah, and if we still look at the list, it didn't change. The value is still there. It only changed this copy variable. That's really cool.
There's one more example that I wanted to show, and that's when you want to mutate multiple things. We can mark a method as mutating and sort of mutate a single variable, but how do you mutate multiple things? Let's see if we can do that. I'll copy the code from up here, and we're going to write a method to move cities, or swap cities. We want to write an extension of Person and write a method swapCities and then pass in some other Person. Actually, we'll use an unnamed label and so how do we do this? Well, the first thing is we have to mark it as mutating because we want to mutate the value. Let's create two values. Person, Chris ... so “Berlin”.
I was actually going to use the Canadian Prime Minister as another example of value, but almost nobody knows his name. It's true though. Sorry. Some people have to put up with this every day with the bad jokes. I'll use Andyy actually. Let's see, Andyy is in “Melbourne”. Okay, so we want to switch cities. I don't know, I didn't ask Andyy, but I hope he's okay with this. There's some error. Okay. Closing brace, ah yeah, here it is. Thank you. How do we do this? Well okay, so first we store the oldCity off, so `oldCity = self.address.city`. We can actually just say address.city. Now we reassign address.city, so we say `address.city = other.address.city`. Easy enough.
Now, if you want to say `other.address.city = oldCity`, we have a problem because how do we know that we can mutate other. Of course we could make a copy. We could do something like `var copy = other`, but then we are just mutating that independent copy and we're not actually changing the variable. This is a really good case for inout. In-out allows you to mutate other variables. You could mark this other as inout, and what that means is that the value gets passed in, I guess copied in, then you can change it and then after the method is done it copies it back out. Let's see how that works.
We could say something like chris.swapCities, and it inserts this ampersand, which is sort of a signal like I'm going to pass that variable as inout. I can swap with Andyy. Now we can dump the two values to see if it worked. Let's see what we got. Chris in Melbourne and Andyy in Berlin. That's how you can mutate multiple variables, or sometimes you don't want to write something as a mutating function but want to write it on another type, and that's how you could do that. You might now ask yourself, "Okay, what should I use? Should I use mutating? Should I use immutable values?" With Swift, you have the flexibility to do both. If you want to create an immutable copy like we've seen before, we could always say `immutableAndyy = andyy`, and now nobody's going to change that copy.
You have this flexibility. This also means that you get to choose. There is no one right way. If you use mutating of course you get the benefit that you can mutate in place. If you use immutable values, you get all the benefits of the functional chaining. You can use whatever you like. You can always create an immutable version out of a mutable version, and you can always create a mutable method out of an immutable method when you're dealing with structs. Don't believe anybody who tells you, you have to use immutable values or you have to use mutation. You can choose and yeah, that's basically all I wanted to say. I hope you have many questions. Find me in the break, or find me in the questions room, and thank you for your time.
If you enjoyed this talk, you can find more info: