Pure functions have no effect besides returning an immutable value. If that’s true, then how can we use them to represent changing state?
►► Audio, Video, and Transcript available: https://lispcast.com/how-can-pure-fun...
►► Subscribe on iTunes: https://itunes.apple.com/us/podcast/t...
Transcript
If our functions are pure, meaning they don't change anything outside of their scope, then how is it that we can use functions to represent change and to represent behaviors? Change over time?
Hi. My name is Eric Normand, and these are my thoughts on #functionalprogramming. Let's imagine that we're already good at making pure functions, doing a lot of work on our calculations.
We're good at making pipelines, all that in-the-weeds functional programming stuff. Very important, but let's say we're already good at it. Why are we even using functions? What can we do with these functions now that we're good at them? My answer is that we should be modeling behavior with functions. Let's back up a step.
When I was younger, less experienced at #programming, and first introduced to #Java, if one of the requirements said that, "Well, this thing needs to be able to change over time," I would probably just have made a setter method for that thing and just said, "Well, this is how you change it. You call this method, and it will mutate this value. It will just set it to something else."
I think that that is about as much analysis as I ever did with what it means to change. I don't think I'm alone in that because I hear people all the time asking like, "Isn't the world mutable? Don't we need mutable state? Because the world is mutable, how can you model things when things change? You need changeable things."
I think that that misses the point. The point is this. If you just say, "Things change," you haven't really captured anything interesting. You haven't said anything specific about how they change. When do they change? Why do they change?
You have this situation where we've got this general memory where a byte can take on any value between 0 and 255 that you want to put in there. You're not constraining it at all based on the semantics, based on your domain, based on what you want that byte to represent.
What functions can let you do is define how things change. How do they do that? One way is because functions take an argument and they return a return value. We're talking about pure functions right now.
They have an input and an output. They don't change their input. We're just not dealing with any mutation at all, pure functions. If you make that input the current state, the current value of the thing, you can make the output the next value of the thing.
You're defining how it changes very precisely. It goes from this current thing to this new thing. Now, you can add another argument and do something like make that argument an event.
You say, "Well, when the state is like this and we get this event, then what is the output? What is the new state?" Or maybe it's, "What is the new state?" You also turn another set of events that get called. They get dispatched.
This is the kind of modeling that we're able to do in functional programming, is take this top-down approach of saying how the entire state changes based on the events we're getting.
You're defining it in such a way that it's totally testable. You don't have to set anything up. You just pass in a current state and an event, and you can test properties of the return value.
There's nothing like, "Do I have to make a new mutable thing and like fiddle with its methods until it gets to the right state, and then test this one new method, and then check if it changed?"
It's much more testable, but then there's this other thing. There are so many questions and so many ways of thinking that mutation just doesn't really allow us to do. You have to do a lot of work to work around.
One of those -- I think it's the best example -- is to think about counterfactuals. Let's say, we do have a real current state. This is the state of the system. The state of whatever thing, entity we're representing, whatever it is.
We want to ask, "What would this thing look like after I receive this event? I haven't received it, but what would it look like if I did?" That's a lot like what a test is. It's a counterfactual.
It's not an actual production data. It's not an actual production event, but we're asking, "What happens in the system when I do this, and I expect it to look like this?" It's a test. It's often also very useful in your production system.