What is a mutation function?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
Mutation functions let you represent changing state over time. They are easily reified, used as reducing functions, and can operate on nested data.
[00:00:00] What is a mutation function?
[00:00:04] Hello, my name is Eric Normand, and this is my podcast. Welcome.
[00:00:10] So in level two, we're dealing with operations. And there's a very important kind of operation that might be unfamiliar to you if you're not used to functional programming, and that is what I am calling the mutation function.
[00:00:28] So a mutation function is a function that takes the current value of a thing and some arguments and it returns the new value of the thing-- a copy that has been modified. So it doesn't actually mutate the argument that you pass it. It returns a modified copy using a copy-on-write discipline. And so it is immutable. None of the [00:01:00] values have changed. They're all immutable, but we call it a mutation function because it's how you represent change of state over time.
[00:01:11] These are very important because they define how you can manipulate your object, how you can change its state, and what are the operations that are allowed on this value.
[00:01:25] Now there's some cool things about it. One is that when you want to eventually reify your operations, so turn them from functions into data that represents that operation, you can see that it's a simple refactoring to make your mutation function instead of taking like four arguments, you have a single mutation function that [00:02:00] takes what you could call the operation, right? Or an event. Sometimes you would call it an event, like the user chose to add mushrooms, right? That's an event. And this mutation function takes the current pizza and the event that they want to add mushroom and it returns the new pizza.
[00:02:23] So there's a kind of interpretation going on. It's a very simple interpretation of that operation within the context of that current pizza. And so this mutation function now is kind of universal for all the operations that are possible. Because you just have one function that takes any of the possible operations with its arguments. That's kind of cool that you can refactor it that way.
[00:02:55] Another thing that you can do-- the further step-- is now [00:03:00] you have a sequence like a list, an array of these operations, and you can run reduce over it. Your mutation function takes the current pizza and an operation . You pass that to reduce as the reducing function and the current pizza now and a list of operations, and now you can apply multiple operations in one go, basically. And the reduce will do the work of returning the final version of the pizza after all those operations.
[00:03:41] And this opens the door for stuff like event sourcing, where you don't keep the current value of the pizza, you keep an initial pizza and a list of all the operations done to it, and [00:04:00] that's really nice for doing undo and other UI niceties.
[00:04:07] This mutation function, it's something we use all the time and it has these cool things you can do with it later, even if you haven't really planned to do them. They're easy refactorings to do later. And they fit in with the functional programming paradigm pretty well because it's a reducing function.
[00:04:28] But also, here's another thing I forgot to mention. It's also an update function. An update- in. In Grokking Simplicity, we talk about this. If you have nested hash maps, nested objects in a data structure, and you wanna modify one that's three or four deep in the nesting, you can pass in a function to update- in.
[00:04:53] You give it the path, like what are the keys down which to travel, and then you give it a mutation [00:05:00] function and the arguments, and it will call that mutation function on the nested value and make copy-on-write copies all the way up to the top. And so this is a nice, convenient way of operating on nested data without having to manually pull it apart and make all the copies yourself.
[00:05:26] Again, it's the mutation function that is passed to update-in. Just like it's passed to reduce, this is passed to update-in. And it lets you operate on deeply nested data in a convenient way. It plugs into a lot of different places and I like to structure my functions this way because of that.
[00:05:51] All right, that's all I wanted to say about mutation functions. My name is Eric Normand. This has [00:06:00] been another episode of my podcast. Thank you for listening and as always, rock on!