Why do we need to model time?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
All sophisticated models need to include time. We discuss two main ways to do that.
[00:00:00] Why do we need to model time?
[00:00:03] Hello, my name is Eric Normand, and this is my podcast. Welcome.
[00:00:10] So today I want to talk about how when you're building a model, at some point your model has to incorporate time, and time is an important part of every domain, if the domain is sophisticated enough, it's complicated enough. I'm not talking about very simple domains, but it's very common that just capturing a single snapshot in time is not enough to do what you want to do, and when you reach that point, you have to take into account time itself, and you have to build your model to take that into account.
[00:00:59] Let me give you [00:01:00] an example.
[00:01:00] So, Apple Contacts, it used to be that it didn't sync. It just stored it right on your computer, on the hard drive, and it was a very simple application. Just a bunch of key value pairs per person, right? So phone number is one key. Their name is another key. Their address, another key. Email address.
[00:01:24] So it was just a very simple application, but then when they started synching, it had all these problems at first. They didn't know how to sync this data, and it turned out that the solution, as I'm arguing, is to put time in your model.
[00:01:42] No longer can you have simple key value pairs. You need something else. You need to at least store the time of the last change. Have the current version refer to the previous version.[00:02:00] So you can see what changed by doing a diff.
[00:02:03] So you need to incorporate this sense of things changing over time right into the model. It can be very simple like that, but it has to be there. It's gonna be there eventually.
[00:02:19] In accounting, every transaction has a time that it's entered into the book. There's two kinds of time. It's the time the transaction happened, and there's a time it's entered into the book because the book is like an appended only log.
[00:02:35] You might say, This transaction, I want to, postdate it back to last year. That's just to balance the books. You might have to do that, but you don't enter it until like January for the next year. Right. There's two kinds of time in accounting.
[00:02:55] Another example is git. git does [00:03:00] not store the current version of all the files. It stores a list of all the diffs that you've made, and it can reconstruct any state within that timeline just by replaying the diffs.
[00:03:20] As a system gets more sophisticated, it has to start incorporating time. That's my conjecture. I just have a bunch of examples and a bunch of negative examples where it was maybe resisted or there was a failure to incorporate time and the model didn't work.
[00:03:42] Now, there's two main ways of incorporating time into your model. There's a lot of ways, but there's two main ways. The first one is to have the history of all the previous states, so you can [00:04:00] remember every state that the system was in and have a big sequence of them and you can always go back. You can date them or whatever and just jump back to a previous time and see what it was like.
[00:04:15] This obvious has obviously has some downsides. One of the downsides is that it can be costly to store if you don't have a way of reducing the redundancy, because consecutive states don't have so many differences in them, right? You might just be changing the user's name, that's one change, and then you change their address, and so each time the only one value is changing in that data structure.
[00:04:45] And so you could have a way of sharing some of that structure with the previous state. And if you do that well enough, you can get pretty good compression on [00:05:00] it.
[00:05:01] What the ultimate compression will look like is simply the other main way, which is a series of commands or the diffs between things. So this is what we said before about Git. This is how Git works.
[00:05:20] You could have a data representation of the commands in your domain. At first they set the size to medium. Then they chose tomato sauce. They added cheese. Then they added artichokes and mushrooms. But then they changed their mind. They removed the artichokes and they added olives. So that could just be a sequence of commands.
[00:05:46] The neat thing is, like we talked about before, each of those commands is like a mutation function, and you can reduce that whole sequence and regenerate the current state.[00:06:00] This technique is known as Event Sourcing where you don't mutate things, you just append to a log of commands that you've run.
[00:06:13] And it's a thing that people do a lot in Domain-Driven Design in that community. It has a lot of success. It's very much like an account ledger where you basically have this book where you never erase anything. You just add to it and you just add a line, add a line, and if you make a mistake, you add another line to correct the mistake.
[00:06:37] Accountants for hundreds of years , they've used this to a lot of success because it really helps you track what happened, because that's always the question, where did my money go? Well, here we have the whole log.
[00:06:51] We didn't keep your money in a box and take it out and put it in as you paid some somebody and as you deposited more, we don't just [00:07:00] take it out and put it in. We write it down. We write down every little thing that happens. And this makes it very easy like the other one where you have the history of states.
[00:07:11] It makes it really easy to do stuff that's typically considered kind of hard to do, like undo, because you have the history, either the history of commands or the history of states. It's pretty easy to just pop off the last command you did and you're at the previous state. That's undo, and if you pop it off and put it onto another stack, you can have undo and redo.
[00:07:43] Another thing you can do when you're in this mindset, it's not exactly history, but you can kind of look into the future without breaking anything. So you can say what would happen. I call these hypotheticals, by the way. They're not actually the future, it's hypothetical future.
[00:07:59] What would [00:08:00] happen if I click this button? I like to use that to do a back button. Sometimes the back button has a label to where you're going back to, right? You're in an app, the top left, there's an arrow to the left, but it also says home. Well, how do you know that they're gonna go back home? Well, what you do is you take the current state and you apply the command that to go back and you get the new state.
[00:08:33] Of course, it's not actually considered the current state yet. It's just a pointer to this thing in memory of the new state. And you read the label or the title, whatever you wanna call it, and you make that the label for the button. One of the tricks of immutable data is that you get to play these hypothetical games.
[00:08:52] I have a talk. It's called Lies My OO Teacher Told Me. It's not anti [00:09:00] OO it's anti the way I was taught OO. So don't get the wrong idea. One of the things that my teacher told me was that OO was good for simulation. But it's very hard in simulation in OO when you have mutable values to ask questions about hypotheticals, which is exactly why you want to do simulation.
[00:09:25] Another thing is that there's this real difference between queries and commands, whereas often the query is a hypothetical. What would happen if I click this now? Don't change anything, please. I just wanna know what it would look like, cuz maybe I'll want to click it. That's really hard to do in OO the way it's typically taught.
[00:09:49] So just to recap: Models, as they get sophisticated, usually require incorporating time[00:10:00] in one of the two main ways.
[00:10:03] You can do simpler ways to, like I said, you could just like put a last time it changed on every field. But it's not very general, and it only works for your specific cases. But the two main ways that are pretty general are keeping a history of states and keeping a history of the commands.
[00:10:25] And this allows you to do stuff like undo, it allows you to have a look ahead into the future. And it lets you see how you got there. It uses a mutation function, which lets you do a reduce to get the current state. You can always rebuild the current state if you need it. Pretty useful stuff.
[00:10:46] Alright, this is all I have to say on this. My name is Eric Normand. This has been another episode of my podcast. Thank you for listening and as always, rock on!