Eric Normand on Functional Thinking
I had the great honor to appear on *Conversations about Software Engineering. *We talked about functional programming and my book, Grokking Simplicity.
Transcript
Welcome listeners to a new episode of the Case Podcast. In our conversation today, I have Eric Norman here. He's the person behind LISP cast and purely functional TV. He's a consultant and he's the author of Grocking Simplicity. Welcome to the show, Eric. Thank you. I'm happy to be here. So, I think you're pretty well known for being a functional programmer in a lot of different languages. What did get you into functional programming and what's keeping you there? Oh, good question. So I started learning functional programming a long time ago when I was still in college. I took a course on artificial intelligence. This was old school artificial intelligence before the machine learning revolution happened. This is in like, I don't know, late the '99 or 2000 or something like that. So it was very old school. It was in LISP and we had to learn common LISP for the class. But first I was very skeptical. It felt very awkward. But by the end of the class, I felt like there was so much more there. The stuff we had written was so much more powerful and easy to write than Java, which is what all the other classes were in. I don't think I could have done it in Java. Those assignments. The assignments were stuff like make a simple logic reasoner. You would give it some propositions in just propositional logic, A and B and not C. You just have a list of these and then a conclusion and you would say whether that conclusion follows logically from that set of premises. You can do that pretty mechanically but it basically involved manipulating this big, long list of all the possible things that you can deduce from those initial premises and just keep expanding it and reducing it, normalizing the forms of each one so that you can compare. You keep your list small and you can just keep growing it and eventually you might hit the conclusion and then you would know whether it was true. That kind of thing would have been really hard to do in Java. You would just have to write so much stuff yourself or after you've learned how to write it in LISP you might go back and say I'm just going to do it using a hash map and strings and stuff like just basic stuff and not try to model it with classes. Anyway, I got interested in it by that and then I had some time. I was in college. I had a lot of free time and one thing I got interested in was writing a game. I looked around and I read something. I think it was by Richard Stallman, the author of EMAX and it was an interview and someone had asked him why is it written in LISP and he said, "Well, LISP is something cool because you can write one in a weekend." I was like, "Wait, I can write a LISP in a weekend and then build my game in that instead of writing it in C which might be fun but it seemed like a cool challenge." I wrote a LISP in C. It took more than a weekend. I could probably do it in a weekend now that I've done it before but the first time took a while and I had a garbage collector and an evaluator and an interpreter and it worked. It did basic stuff. I never got around to writing the game. I just got fascinated by this LISP and anyway, all these things sent me down this road combined with my contrarianism, my natural contrarianism, wanting to do things a different way from what everyone else was doing, everyone else in my school was using Java. I started doing my class assignments in LISP and found that I was doing them more easily than the people doing Java and I don't know, that's where it all started and I had to go down a road of basically learning object-oriented programming better than I had so that I could unlearn it. What am I actually doing here? Why are these classes here? Just really introspecting and researching what is going on here and realizing where it's good and where it's not so good and uprooting the whole that had on me where I just would do that by default and get more and more into the functional stuff. Oh, nice, okay. You already said you're a little bit of a contrarian. I noticed when reading your book that sometimes you use different words for concepts that I know from different fields and one of them is that you're talking about functional thinking instead of functional programming. What's the difference between those words or are they the same word? That's a really good question and it actually has a really complex answer. I don't know, well, I'll just go and explain it. I wanted my book. When I first started thinking I'm going to write a book, I wanted the book to be called Functional Program, like full stop, no subtitle or anything. I just wanted this to be the book on functional programming and I've written a lot of blog posts and done a lot of SEO and so you just write your post the title of what it is and just want to explain to people what it's about. But the publisher had a different idea. They actually thought that using functional programming in a title is bad for marketing for sales. People don't want to read a book on functional programming. We disagreed and so I think functional thinking was the compromise. His idea was we're not going to tell them it's about functional programming but it's about how to think like a functional programmer so it's going to be about functional thinking. I actually do like the term functional thinking though. It's focusing more on the concepts and the sort of mental skills that are necessary when doing functional programming and that you're approaching a problem from a different way instead of what a lot of functional programming books are about which is more about what the code looks like and what techniques or how to use some functional library or something like that. That's not really what the book is about. But still the title of the book is Grokking Simplicity. Once the connection to simplicity why is there simplicity in the title and not functional? Again, a complex answer. I wrote several drafts of the first couple chapters, first three chapters really, before landing on the format that it's in now. One format which actually got really far in polish in one of the drafts was the introduction was going to kind of justify functional programming like what is the reason it exists. It was an analysis of complexity in software. Why does software get complex? It was actually a really deep analysis. It was all about, I identified two big sources of complexity that the book was going to tackle. The first one was this complexity that happens when you have multiple threads or multiple concurrent operations happening, which happens in JavaScript. You don't have multiple threads but you have all these callbacks happening, all these events happening concurrently. You don't get to control which event happens next, for instance, which Ajax response comes back next. So because of that, you have this explosion of the ways that they can interleave. Actually, if you do the analysis, it's factorial. It grows factorially based on the size of the number of steps that you have in each timeline. Factorial growth is really big, so that means that you have to know as a programmer that all possible pass, because any one of them could happen when a user does something. Any one of them, any one of these factorial, we're talking about, you have 12 steps in a process and two of them running at the same time. It's already a million different ways that they can run. So this is what the chapter was about. You've got a million different ways that they can run and your job as a programmer is to guarantee that all of them are correct. And if you put a 13th one, it's at like 7 million, right? Because it's factorial, it explodes really fast. So this is hard and the complexity is more like algorithmic complexity where you're kind of analyzing not, it's something measurable, like how many steps does this take as something grows, right? So that was one source of complexity. And the other source of complexity was with branching. So if you have an if statement that implies two branches, right, one where if it's true, the test is true and one where the test is false, and if you have two in a row, they multiply. So you have four. You have three in a row that's eight different ways that something can happen, right? You have eight different paths through these three branches. So it's multiplying. It's like an exponential increase in the number of branches. So the question is, are all of them correct if any of those branches can happen? Every time you add a conditional, you're potentially doubling the number of paths. We want to reduce the number of conditionals, right? And one source of conditionals is having a poorly fit data model. This is my attempt at solving the problem. It's like, you're going to need conditionals. Like, some fields, some domains are really complex. Okay, so my solution was to have a better fitting data model, because every time you have a corner case, you need a branch to determine whether you're in that corner case. This was my idea, right? And I was going to spend a lot of time talking about how to do a better fit data model. And both of these ideas, this idea of managing the complexity of the interleavings, of the number of orderings of different timelines and data modeling, I think that they come from functional programming. Like, you don't see this that much in, say, object-oriented programming. And that was my initial idea. I think the book would have been very boring if I had started, if I had actually published those, that draft. But it was good for me to have like an underlying understanding of what the real problem that I was solving was, you know, proposing a solution to. And I eventually came up with a different format that I think is better at teaching. It's a lot less theoretical. It's more like, let's get into the coding. And so I don't actually talk about those things so much, that complexity. And so it's, I think that the problem is the disconnect is there. It's not obvious that it's about complexity and it's opposite simplicity. Because I don't talk about it so much when I'm actually teaching the thing. So it is a good question. And it's like it's got a historical reason. And the title was chosen by the publisher because that's their job, right? They do the cover and the title and that's all the stuff that's the marketing stuff. And the publisher had read this other draft and hadn't read the new draft. And so didn't know that it was a totally different kind of book. Interesting. No, he's busy and I didn't know that at the time that he hadn't read it. But he proposed this title and I don't think it's a wrong title or anything. No, I didn't mean that. I wasn't implying that. It wasn't the title I wanted, which was functional programming. Interesting. So much in the title already. So one thing that I found interesting is in the forward guys still talks about organizing side effects as like the main thing in functional programming, right? But in your book, you call it implicit and explicit inputs and outputs. Why did you choose those words and what do you mean with those for those people who don't know what side effects are? Right. Okay, so side effect is a term used in functional programming to talk about stuff that happens when you call a function that is separate from the arguments and the return value. So the idea is that the function is supposed to be like a mathematical function. This is the idea that functional programming is where the term comes from. Like, you call a function to get its return value. And anything else that happens is kind of the side effect that you have to worry about. Just like when you take a medicine, you take the ibuprofen to get rid of your headache, but maybe it makes you drowsy, whatever is the side effect. And so your function might have the side effect of mutating a variable, a global variable. Or it might have a side effect of sending an email. Okay, so that's the idea of side effect. In my book, I want it to be a little bit more clear that there are side effects that I want to cut it in two. There are some side effects that are getting information into the function. So you might be reading from the database, right? And there are some side effects that are sending data out of the function, right? So you might be sending an email or writing to a file or something. And because they have to be mitigated in different ways. So an implicit input, you want to make that into an explicit input. And the explicit inputs of a function are its arguments. So instead of reading from the database in the function, you read from the database outside of it and pass in the value you get from the database as an argument. Likewise, instead of writing to the file, you would return the string or whatever that you would have written to the file. And then the thing that called you would save it and then write that to the file. So that's a way of kind of explicitly laying out the process of this refactoring, making something into a function without side effects, which I call a calculation. Okay. So in the book, you say that there are calculations and actions, but the third actor in this play is data. So what is data and how like our data, you also talk about events, is all data events or what's the connection there? Right, so if you look up data in the dictionary, which I feel like you have to do if you're writing a book and you're going to use a term like that, one of the definitions is facts about events. And I think that that really captures the essence of data in a computer program, in a software engineering. We're building information systems, we're getting information, you know, information is coming into the system, we're doing something with it, and then we're sending more information out, right? Maybe in a different form or something. Your question is, is all data events, right? So let me explain that because a lot of people ask that, they're like, well, what if it's not an event, right? Like I have data about a person. And so that's not an event, that's an entity in the world, it's an object, you know, it's not an event, but where did that data come from? You know, maybe it was from someone submitting a form on a website. And that receipt of that, that request, that form post is an event. All you know is they, they, someone filled out this field, this field and pressed submit and you got that. You don't know if that actually corresponds to somebody's real name, that's just this thing that they posted, that's all you know, right? So it is an event and we often forget that. And so I wanted a definition that highlighted that, that's all events, right? And we, we make assumptions, we have to, that like, well, the data I get, it's garbage in garbage out, if you give me bad data, I can't like figure out what's the truth. But it's all events and we, we do transformations and we make assumptions so that we can interpret that data, but are those events, but it's all just facts about the events. You know, you could have an event like, well, I read the thermometer at this time and it told me this, you know, that's all you know, you just have some sensor data to fact about what happened at that time. Doesn't mean anything about like you have a true idea, true, true understanding of the actual temperature. It just means that that's what the sensor sent you, sensor could be faulty. They could have been at a weird time where like, you know, a piece of a drop of water fell on it, you know, it's all sorts of problems. So you have to remember that these are just, you know, moments in time and data, the facts about what, what happened. Yeah. Okay. Cool. So, so we have our calculations, our actions and our, our events, right? So one of the examples in the book that I really liked is from real life. So I want to go grocery grocery shopping and I want to know what I need. So I go to my fridge, then I walk to the store, I buy something, I go back, right? If I, if I describe it like that, then all of those things are basically actions, right? They all have side effects. So how does functional thinking help me to find calculations or maybe events in this procedure? Right. So what, what I think happens is that the stuff that's usually that is that I'm calling calculations, all these functions without side effects, they're happening, but they're all in the person's head. So they're invisible, right? The driving to the store, putting stuff in your shopping cart, all that stuff, you can observe someone do. What you can observe them do is think, well, I have, you know, I have orange juice and milk and tomatoes, but I need, you know, orange juice, milk, tomatoes, carrots, onions, etc. What do I need to buy, right? They make this calculation in their head. This is, it happens and it might even be instantaneous. Like they don't even realize they're not doing it consciously. But when you sit down to think about how to program it, you know, obviously something needs to calculate what to buy based on what, what you have and what you need, right? And so the, the calculations are often, if you're using this kind of real world process that you're trying to break down, the calculations are often thought processes. And what's interesting about thought processes is just thinking about what you need does not affect the world, right? And so that shows you that the calculations, you know, can be run, you can run them twice. You can do the same calculation twice. You should get the same answer. And it's not going to change anything. So it's safe to run them twice as well. The question about where do you find the data? The data is often the result of this calculations, right? There's like kind of in between data. Also actions can generate data. So like you look in the fridge and you see what you have. Everything in the fridge is an action because you, if you do it at a different time or on a different day, you'll have different stuff in your fridge. The fridge is changing all the time. And so it depends on when you do it. And so it's an action. But it generates sort of a list, you know, if you were going to make a data structure out of it, it would be a list of what you have in your fridge. And you have a list, maybe it's on a piece of paper or in your head. Another is, but it's data of what you need. These are the things I want to have. This is what I have. And now these are the things that you need to buy, which is like the difference, right? And a functional programmer gets good at seeing those, those invisible calculations and the sort of transient data that gets, that is the inputs and the outputs of those calculations. So I think this is a good example because if we look at a fridge, then another person, which would be a process in programming probably could go to the fridge and get in, eat a piece of cheese and close the fridge again while you were shopping, right? So the content of the fridge could change since the last time you looked at it. So when I talk to people that are really into functional programming, apart from side effects, the other thing that they always talk about is immutability, right? So how can I do something like modeling a fridge immutable? Because I need some, like I need to have side effects. I need to have immutability, otherwise my programs wouldn't do anything, right? Right, right. So that's a really good question. This is one of those, we're talking about maybe in a little contrarian. This is where I think my contrarianism generated a little insight compared to the typical view of functional programming. I see, as a professional functional programmer, what I do and what other people that I work with do, we do use mutable things all the time. So we do have mutable state. We recognize that we need it. And so we would represent the fridge as a mutable thing, a mutable variable or something like that. The difference though, is that we recognize that when I look in that fridge at time X, and I make a list of what's in there, when I go to the store, that list cannot change. That is what I saw at that time. If my friend, my roommate, eats my cheese while I'm at the store, that shouldn't change my current list, that would get very confusing very quickly. And this is what the whole problem of concurrency is, because now I'm going to come back home and there's no cheese. I was just at the store, I could have bought it, right. So you have this problem no matter what. There's no way to solve it. They could text you, I just ate the cheese. Well, where are you on your shopping trip? Are you checking out? Are you on the way home already? Like, there's no way to solve it. You're going to have to go to the store again, right. So it's sometimes, maybe they catch you at the right time, you're in the cheese department, you know, and they text you right at that time, you're like, oh, that was nice. That happened really smoothly. But notice, this is another piece of data sent to you, right. This is like, you know, a fact, we're out of cheese, right. And so you can modify your list based on this new fact coming in, right. So like, just want to make that clear. Nothing is mutating, right. You still remember, well, I had this list that included cheese. I thought I had cheese, right. At this time, I did have cheese. Now I'm making a new list that's subtracting cheese from that old list I had. So wow, it sounds really complicated. Of course, when you describe all this stuff happening in a computer's memory, it can get really complicated. And the question was about using mutable things. And so what I think is the insight that I tried to bake into this book is that functional programmers do use mutable state. And we have a lot of tricks to make it easier to work with that other, you know, people programming and other paradigms don't use. And so I'm trying to kind of flip it where people typically say, oh, functional programming is all about no side effects, everything's immutable. And I'm saying no, it's actually the opposite. The no side effects and everything is immutable. That makes programming really easy. That stuff is so simple, like just finished, you know, you can just put stuff in there and never worry about it. You test it once. It works. It's never going to change. It's fine. Now let's focus on the hard stuff, which is that you're sharing a fridge with your friend. You can both change it at the same time. I mean, not at the same time, but like without each other knowing that you're changing it. You have this mutable thing that's shared. How do you share it efficiently and productively so that, you know, you're not stepping all over each other. You're not, you see what I'm saying? Because you might have a what I call a what we call concurrency primitives, right? So like sharing a fridge is different. Let's talk about sharing a bathroom, right? So the typical way you would share a bathroom is you have a lock, right? Like, if I'm in there, I'm going to put the lock and now no one else can come in while I'm in there. Like a one at a time kind of situation. And usually that works well enough. If you got two people sharing a bathroom, you know, if someone's in there, you just come back later, right, you just try again. That probably wouldn't work so well if you have 10 people sharing the bathroom. Because, you know, there's going to be now like, well, I came back and it was busy with someone else. Like, I should have come back. You want to wait there and just stand there. And now you're wasting your time. And now there's a, you know, there's all this contention for this shared resource. So you'd probably have to come up with a better concurrency primitive, which might be something like a queue, right? So like, okay, I'm going to put a token, my name, like a little, you know, piece of cardboard with my name on it in line, right? There's some line next to the bathroom. And so when that person comes out, when the person in there comes out, they're going to call the next person on the list, right, the next person in that queue and say, hey, it's your turn. And then they'll come in, right? And then, you know, move their thing out of the queue. And so you have a system that is, is somehow fair, more fair and it's going to work for this larger contention, it's a larger amount of contention. Especially if you had more people sharing that bathroom, you need something different, like a schedule, like, hey, you get to use the bathroom, especially like in the morning when everyone's getting ready, like, you get the bathroom from this time to this time. If you miss it, you miss it. But it's, it's fair, you know, and you can kind of work out, well, I needed earlier because I go to work earlier and whatever, you can work it out. That might be a better system for those times. So these are concurrency primitives and you can code these up. You can program these. And what it turns out that they are is, I mean, what I, how I see them is you're building a new model of time. You're building a new semantics for how this bathroom, how time works for this bathroom, right? Time, you know, time in a programming language is it has a semantics and you have to build on what the programming language gives you to build a new model of time that better, better models the way you need it to run because it's not, you know, by default, it's unlikely that all the stuff that you need to program happens the right way in your language. That's interesting because like one of the principles you also talk about is defensive copying. Is that in this category or is that something different? The defensive copying is a tool, it's a technique that it works well. So if you're trying to do, you know, maintain an immutable discipline, but you have to share data with a system that doesn't implement the same discipline or might not have any discipline, like, so, you know, we often talk about legacy code, which is code you can't change right now, you don't have the time to change. And it's often written with older practices or whatever. So it might, you know, you want to pass it a hash map of data or an array of data and it's going to modify it or maybe you don't know, maybe it will modify it, maybe it won't. You don't trust it. So what are you going to do? You have to copy the whole thing and send it the copy. It's the same data, right? It's the same facts, but you're going to give them a copy that can, they can do whatever they need to do with it. They're going to add and remove stuff from the list. That's fine. You have your original with you and you kind of lose the copy, the pointer to the copy because you don't want to accidentally, you know, use it later because you don't know how it's been changed. Okay, cool. One thing that is currently keeping me thinking about different things is like one of the ideas you also mentioned in the book is the idea about facts being immutable, right? And that you, that you look at the facts and then you can see your current state from the facts if you look at them in order, right? And there are different architectures, like the copper architecture, which put that quite far, right? Make it like a core principle of the entire thing. But especially like I live in Germany and like GDPR or other, other, other rules and laws that we have here, they say that we have the right to, to change things. Like we can delete our data. Right. Right. To be forgotten. Yes. Exactly. Like if I have the right to be forgotten, then like facts, like they can be changed, right? They can, they need to be deleted, right? Do you think that there is a, the problem and does it make those principles not apply in those scenarios or is there a way around it? I think there's a way around it. So I do, well, huh, there's, there's a lot of angles to this. But one approach that I've seen, which is, it happens in the Datomic database, which is an immutable database. It's a penned only. So it remembers the whole past, the whole history. And then if you want to make a, you know, a change, let's call it that it just adds a new, new row, right? So like if you want to change your name, you know, you had a mistake in your name as miss spelled and you want to say, wait, my name is actually with a C and not a CK or whatever. So you, you make a new row that says, you know, on today, he, this user said his name was Eric, right? And so boom. Now I know, okay, from, from now on, I'll count you as Eric. What, what you're asking is, well, I want them to forget my name, right? So what the solution is in Datomic is it leaves the row, but it deletes just the name. And so I still have the data. I still have the record that he changed his name, but I don't know what it was, right? And you delete all of them, you know, all the records about my name, you know, not, you don't delete the record. You just like blank it out, like, oh, he changed his name here, changed his name, but I don't know what he changed it to. And so the data is gone from the database to actual, the actual personal information is gone. But the, the structure of the database is still intact. And you know, it still preserves all that other information that you were trying to preserve, which was, you know, the fact that things were changed. I don't, I don't, I haven't done an analysis to see if, you know, that actually implements the, the laws of GDPR. But it seems to me like a good, a good first step for how you would do something like that. Well, yeah, I guess that's my answer. Okay. Cool. One of the things that I've found interesting in the book that you claim that the ideas that you're proposing that they are applicable in all kinds of languages, that you don't need to have a functional language to apply those ideas, right? And you show that by using JavaScript for all, for the entire book, right? So my question is, do you think that there are also ideas from object oriented programming that you should bring over, like one of the ideas from object orientation is, for example, information hiding. I applied those ideas to function programming as well, or is it not possible? Yeah, I don't think that they're incompatible. I mean, it's a, it's a good question. Just a note on. So I use JavaScript because I didn't want, I wanted it to be accessible to as many people as possible. JavaScript is readable to anyone who knows Java or C or anything like that. But I also didn't want to use a functional language that where it's, it would seem like you were just learning the features of that language and like, oh, if I don't have that feature, then I can't do this skill that he's teaching. I wanted it to be where you learned to build the skill or build the feature yourself. And so that's why I used JavaScript and it turned out to be really great for teaching because it doesn't, it mean has, it has enough to do functional programming, but not all the stuff that you want. And so, you know, basically if I had chosen Haskell, like, well, immutability is baked in, pure functions are baked in, like all this stuff is just there. And like it just, it's great. It's convenient if you're, if you are a functional programmer, that's what you want. You want it to just be default. It makes it easier for the programmer. So it's better to program in Haskell if you want to do functional programming, but it's not as good for learning and certainly not as good for me teaching because like, I would have to show you how to build Haskell, which would be really hard. Okay. So you're, you're also asking whether there's a lot of stuff from OO that would also be applicable. And I think the answer is yes. I think there are, there are a lot of great things in OO that we use as, you know, functional programmers, you know, we kind of live in an OO world, you know, people, meaning, meaning the dominant programming paradigm in most, most places, most languages is object oriented. And it's very useful. It's very useful to have the kind of open polymorphism that OO has. And those things are very, very cool. And one of my goals was to show that the ideas of FP are applicable, like immediately, like if you're having some trouble, some code is buggy, you can start refactoring it in these ways that FP programmers think and it'll start improving the quality of the code. And you don't have to go all in like, okay, we got to rewrite in a functional way. I wanted it to be like, let's start with some typical, I mean, we're doing, we start with procedural code in the book, let's, this is like standard procedural doesn't look like anything fishy, but from a functional perspective, there's a lot to improve. And yeah, we just, we start from there. The idea of information hiding, I think is valuable. We talk about something very similar, which is called an abstraction barrier. That's sort of how we talk about it in functional programming. And the idea is there's a lot to keep in your head when you're dealing with a, when you're just programming in a system, you have to understand like, oh, what data structure is that? And like, how does the API for that work and like what, what, what methods are available for this API and that thing? And so you have a lot to keep in your head. And you want some way of kind of cutting it up so that you can get, you can, at some point, you don't have to understand the rest. You can just say, okay, I understand that when I have this object, I have these seven methods on it and I can call those and I don't have to care how it works on the inside. I just trust that it works, right? And that's an abstraction barrier. It's like, I don't have to, I can ignore the details past those methods that I call. And so we do that in functional programming. We create a barrier where you say, look, if you just stick to this interface, these functions, you don't have to worry about how it works. You don't have to worry about what data structure I'm using to implement it. You don't have to worry about, you know, all the details. You just stick to these functions in the interface and you're, you know, you're good. And so in the book, we use it as a way of, you know, practically it means two teams don't have to communicate as much, right? It removes the dependency between the teams because they negotiated this interface. It's like a contract, right? We're going to make the functions you need and you don't have to worry about how they work. And you just use those functions and we'll trust that you won't mess with anything else. You'll just call those functions and we don't have to talk except maybe to renegotiate if you need a new function or whatever. And so the marketing team and the programming team can now coordinate because the marketing team has to write custom sales routines, but they don't need to know how the shopping cart works. They just have this interface for it. Nice. Okay. So one thing that you just said was that in your book, you showed how to implement certain things that are available in a language like Closure or Haskell, right? One thing that I really liked in the book was you really showed like how do I implement copy on write if it's not there, for example, or how do I use structure sharing? And I think that's really valuable. And you also say that you should probably not implement all of that from scratch, but use a library for it. There are different libraries in the JavaScript ecosystem, especially nowadays where people use immutable JS and libraries like that. What do you think are people missing that are using JavaScript using one of or two function libraries in comparison to using a language like Closure, like what do you think people gain if they really move to a functional language? I guess there's two things. One is you don't have to maintain the discipline yourself. You can relax, right? Everything you make is immutable. You don't have to like double check that the thing you're passing it to is going to mutate it. It's not usable, like there's no way to change it. The other thing is that it's part because it's part of our ecosystem in Closure. All the libraries do it too. So you don't have to like, you know, if you were going to do it in JavaScript and you're going to use some random NPM library, you don't know what it's going to do. So you would probably have to do like a deep copy, a defensive copy whenever you use this thing. So you have to be kind of vigilant all the time, whereas in Closure it's because it's the default, it's also the default of how libraries work and it's just everywhere. The other thing is it means that concurrency is a lot easier because, you know, immutability makes concurrency easier, it makes sharing easier. You know, I often say that immutability is like the default in the real world. I know things change in the world, but when we're dealing with information, the information is like written on a piece of paper or something in the real world. And the paper doesn't change by itself. And if I need to share that information with someone, I put make a photocopy of it and I give them a copy, right? So if I put a piece of paper in my pocket, I don't expect it to change. Even if I shared it with someone else, right? And this is the kind of misconception that I think people have because they're like, oh, but my, you know, my age changes or my, I can change my name, I can go and have my name changed at the courthouse, right? And all that's true, but the data about what your name was at that time shouldn't change, right? Yeah. The record that we have of your name has not changed just because you went to the courthouse. Like they want, they want, they somehow want to mirror, okay, let me put it another way. All the information systems in the real world, like the precomputer information systems that that keep good records always focus on immutability. They go to great lengths to make things not change. So if you go to the doctor and let's say you have the flu, they're going to write that down. They have the flu, you know, prescribed, you know, some medicine and they're going to come back in two weeks. Okay. They come back in two weeks. Do they throw the record away? They're like, oh, they're cured, cross it out like no, never had the flu. No, they write a new record that says, okay, they visited again, no flu, no symptoms. They're good. You know, and then you put that in the file too, right? You remember that they had the flu and you remember it. So you are changing, you get the flu and then you're healed. But so you're changing, but the records shouldn't change and they go to great lengths. There's all sorts of like systems of files and how do you, how do you store this and like, how do you write it so that other people can read it? Like all this stuff, it's the same with accounting. If you like spend $10, they don't cross out your balance and write it, write a new balance or worse, like, white it out, you know, erase it and write the new balance, no, they write a line. They deduct it, they took out $10 from their account. And so then they have to sum it up, they balance it at the end of the day. So all these systems, these information systems, which is what we're programming in usually in a software engineering, they were not like, I guess, simulating the world. We're simulating the information system, the folders and the files and the papers. That's what we're trying to simulate. And all those, they go to great lengths to try to keep those records as long as possible. And they all have this append only discipline, not mutability. So I think that that's something that like, I mean, if I'm going to, if I'm going to be ungenerous, that the OO object-oriented programming kind of, I want to say damaged are thinking that I think OO is great for a lot of stuff. But the idea that you're going to model the person and all their changing attributes as fields on an object, that's not what you're doing. You are modeling the medical record and the doctor's visit and the notes that the doctor writes during that visit, that's what you're modeling and the OO analysis and design has taught us to model the person. And that's not correct. And so it's caused a lot of damage. And I think the FP people have been, you know, not immune, but just avoided that damage, that we are still programming information systems. I find it interesting that you describe it in that way and that you also refer to OO there, because I think there's something similar in databases, right? Like if I look at a typical database, no matter if it's an SQL database or if it's MongoDB, like if I change things, then it is overwritten, right? The old record is not there anymore. So maybe it's more than just object oriented programming, maybe it's going beyond that, right? So databases are really fascinating. So when all the databases that we use today were kind of seeded, you know, the original ideas were like in the 70s, when disk space was really expensive, right? And so you couldn't afford to keep the record. You just couldn't afford it. I mean, this is the same problem with in the year 2000, right, the Y2K problem, like they were trying to save two characters by not having the one nine, right? And they caused all this because it was cheap. That those two characters with millions of records, that was expensive stuff, right? Now that we have really cheap hard drives, you know, long-term storage, it doesn't make sense anymore. And the databases know this. They're mostly journaling now, right? They do add like a record, "Oh, this row changed, this row changed, this row changed." And then those are later kind of rolled up later in a batch sometime later. So databases, the database implementers now know that it's cheap enough to journal basically, like this, what do you call a right-ahead log, right? They're appending all these changes to the database instead of actually on disk, like changing my name. They write a record somewhere that says, "Okay, the name has changed to this now." And then later, it kind of gets rolled up and eventually persisted to disk like in the record itself. And they're only doing that to maintain the, you know, backwards compatibility. So then all the software that already assumes that the database is immutable and still work. Like the question is, what would a database look like today with, if it was designed today where you wanted cheap, or you have cheap hard drive, right? Basically, you know, that's like the definition of big data, right? It's cheaper to keep it around than to figure out what to delete. So you have cheap storage and you know that it's actually pretty efficient to store this log and you won't have to do the roll up. What would it look like? And then that you want to keep these records around forever, you know? If you're writing an accounting system, you don't want to delete the whole stuff. You want to keep it around because you never know when the tax person is going to come and say like, what were the records, like what did what did the account look like on December 31st, you know, you want to know that. So it would be it'd be an interesting thing to do to say like, how would we design a database now? Yeah. Yeah. I think that's also interesting. Like I asked you a few minutes ago about like GDPR and stuff. And I think most people don't think about that like their database keeps a writer headlock. So even though even if you delete a person, it's still in the writer headlock, right? So it's just hidden from you like as a developer, right? That it's right. But it's still there. And you could probably access it somehow. Yeah. API to get to it. But also in the GDPR, if I'm not wrong, there are provisions for like it's a you deleted in a reasonable amount of time. So like the reasonable is up for interpretation, right? Yeah. And we're like, oh, we've made the delete. And then the writer headlock will eventually be compacted, right? Yeah, it'll compact it into a onto the disk. Yeah. And that's probably a reasonable amount of time. Yeah. I agree. One question that a friend of mine asked when I said that I will talk to you was that he asked because you have experience in both closure and Haskell, right? How does the type system influence your programming style? Like, do you write a different kind of functional programming in those two languages or are they the same? But the type system is orthogonal to what you're doing, right? No, they're the styles of programming are different for sure. I think the basic principles of functional programming are the same. And I kind of do think that the type system is orthogonal to FP. I think that a lot of people would disagree with me on that. They would say like, no, you need a type system to do FP. But then that excludes so many people from FP that it's not really fair. You're just like arbitrarily excluding them because you don't like that style of programming. So I think the advantage of the Haskell type system, it's like having a logician on your shoulder. It's basically a logical system of logic that checks for consistency and says, well, you said you would pass this only a string and you're passing it a number here. Like something's wrong. And often it feels annoying, but then at some point you kind of like get to know this logician on your shoulder and you can avoid the problems ahead of time, right? So like, okay, I know what he's going to tell me here. So I'm going to avoid it. That process for me took a long time. I think about six months of full time work in Haskell before I felt confident that the code I would write was going to pass the compiler. And I often had these things like, why not? Why don't you like this? Like, and I, it just required a lot of, a lot of experience, let's say just, you know, fighting the compiler it felt like until realizing like, okay, he's right in this system. This is not correct. But what I, and I think that's, that's very important that I said in this system, it is only one possible system and I don't think there is, there is only one correct system. And that is one of the things that I think in the untyped language like closure, we recognize that like sometimes the system that is enforced by the language is not quite the system that you want, you want something, you know, equally rigorous, but different. And we have made the compromise that we would rather implement the system ourselves in our heads, be that logician and have two logicians like, Oh, for this part of the system, we're going to use this logician for this part of the system, we're going to use this logician. And that comes with trade offs like, you know, again, it's a discipline on you to do it correctly. A lot of people aren't going to follow any particular discipline that makes any, you know, that's a sound logical system, you know. And so you see a lot of that. But you know, for my personal experience, this is, you know, this is me, I, this is a huge debate and I don't feel like I'm going to be able to solve it anytime soon. But from my experience, it is easier to learn the Haskell type system, internalize it. And then when you move to closure, you have it in your head and implement it as a discipline. And sometimes you implement it wrong and you fix those bugs, like it's, it's not perfect. It's not like a compiler that's like every timing, it gets the system right. But get a lot of the benefits from having that discipline in my closure code, then, okay, so that's one system, right, or one process, right, learn the Haskell type system through trial and error through fire, trial by fire. And then when I'm in closure, because I've internalized it, I can do that. I can think that way and I can make my types line up. They're all in my head, right? It's not, it's not part of the language, but I'm doing it right. Versus the other way is to learn all these cool tricks you do in closure that are, you know, I guess you'd call them more dynamic, right? And then go to Haskell and try to implement them and you're like, oh, but the type system doesn't like it and I can't, there's no way, like, I can't figure out a good way to implement them so that the type system likes them. That's a lot less fun and I don't even know if it's possible and that was one of the problems I had at first was like I do functional programming and I consider this a functional programming, you know, technique doing this like dispatch on type and doing all this cool stuff. And I'd go to Haskell and it's like, let me try this way, no, it doesn't work. Oh, I get why it doesn't work. Let me try it this way. Oh, no, that doesn't work either. Okay, let me try this one. No, it doesn't work. Well, I'm just not going to do that, right? And so I felt like I had to drop a lot of the lispy, closiery skills that I had to program in Haskell. The other way, I feel like I've got the benefits of Haskell, some of them, 90%, 80%, something like that. I've got them when I'm programming a closure plus I've got all the other stuff. So that's, that's my experience. It's why I prefer closure. But I do see the problem. I see when I look at other people's code, I'm like, I don't like that you're returning sometimes a number, sometimes a string, sometimes an array or they know a vector and sometimes no, like, come on, pick a type like this, it shouldn't have so many types. So you're basically saying that this logician on your shoulder, you make it like a logician in your head that you can ignore if you want, but and you could sometimes forget about it, but it doesn't need to be there and watch every step. Right. That's right. Well, yes. Yes. Yes. Yes. I think that that's a good way to put it like most of the time I'm using it because it makes a lot of sense not to return strings and numbers from a function, like, what is the function supposed to do? Like, you know, if it's got all these weird, this is again, like what I was talking about with the complexity, like this returning a number means something different from returning a string. Maybe there's a better way to encode that meaning in some bigger construct than just the class, the type of thing that you're returning, like maybe you should have a record that says, you know, that has some name for like the difference between the string and the number because what invariably happens is, you're like, okay, I'm going to return a string and that means it's an error message and a number means it's a success code. And then now you, you know, and that works fine, you know, you can write the if statements and it works, right? But then later you're like, oh, but now I need to write a success message. The code is not enough. So now I have a string for an error and a string for like, how do I encode that? Because I just did it based on the string versus number. Now it's string versus string and there's no way to know, right? Or maybe you put error colon and success colon. And now you're parsing strings, like that's terrible. Talk about if statements everywhere and what if you return a string without an error colon in it? Like, you know, you have this other problem. So you need some bigger type, right? Maybe it's a record that says status success, you know, code for message and then the string. And then the error is status error message string. Okay, so what I find interesting in the description here is that I see a tiny, like a tiny difference in what you said about functional programming compared to like OO programming or however you want to call it and in type systems and untyped languages because I asked you if I use JavaScript and I just follow those principles, why do I need a language to support me, right? And then you said, yeah, but then I don't have to think about it all the time, right? But if it comes to type systems, you have a slightly different opinion, right? You say, okay, I can just do it in my heart. Why, where do you see the difference in those two aspects? That is a really good question. So one has to do with the complexity of it. So if I'm doing immutable data structures and it's by default, so I'm enclosure, right, and as opposed to JavaScript, and I'm using only immutable data structures and I'm passing them around and sharing them. I am eliminating a huge class of problems of the sharing because the types of problems that I'm eliminating are you're writing while I'm reading or I'm writing while you're reading. So I'm reading it kind of in between, you're not done yet, you know, it's like I'm, let's say you had to write the score of a sports game on a blackboard and it's a sports game where you have like a lot high score, right? So like let's say you're writing 172 and I'm a reporter and I'm going to read this thing and report to my newspaper, like the score or the radio or whatever, I'm reading it for the radio. I look over and you've already written one seven and I'm like, the score is 17 and then you write the two and I'm like, oh no, I mean 172, like that's the kind of problem that we have in software and it's invisible because you can't see that that happened, right? But you can imagine it happening where someone is writing to this array while I'm reading from it. I'm like, there's 14 elements but you're still adding as I'm saying that, right? So you avoid this huge class of problems and this is one of those class of problems that's multiplicative, right? The number of ways I can get that wrong increases factorially, right? As opposed to with types and like let's say getting the type wrong and learning about that at runtime, it probably does increase more than linearly but in my experience, those bugs would surface very quickly. So as opposed to like this one in a million chance that I won't see until production and I can like have no way to reproduce on my local machine, I am very likely to be able to reproduce someone returning an int instead of a string from a function. I can make that function return the int and see that. I think that that explains why I'm comfortable with dealing with the discipline of types in my head that it seems to me like something that you would catch at time. Yeah, I think that's interesting like if someone does JavaScript programming, then some people will go to them and say like you should probably do type script instead because then it's safer, right? And you ask why and they say because at any point you could return a number instead of a string and I'm like, but I don't do that, right? Or if I do it, I catch it right away. I mean, I think that there's a lot to that. I mean, I think that there's also a lot to having it caught. Like that's, there's huge and I like type script. I think it's great. I think that this idea that it's optional, you know, you can kind of incrementally tighten down your code base. I think that that is very powerful and it's sort of like the best of both worlds, I would say, that you can enforce it if you want, but you're not forced to like even a program that doesn't type check with type script can still be converted to JavaScript just fine and still run. And if you leave out types, it just doesn't try, it's just like, well, okay, I can't do anything with this. But if you put types on this function, it will try. And it will tell you problems that it finds. And I think also the other cool thing is it leaves room for a second type checker. Like you could imagine a second type, you know, optional typing system that had a different logic to it that would be compatible with TypeScript, meaning they could both run. Like you could write a super set of TypeScript that had different types and different kinds of annotations that converted to TypeScript. And then the TypeScript checker would check and then that would convert to JavaScript. Like you could imagine that. And so it would let you use the types that work in different parts of your system differently. I mean, I think like this is going deep into the debate. I do want to bridge these two things. I think we don't talk enough. It really hurts me. It pains me that the two sides of the debate can't listen to each other and communicate. And I'm trying to listen to both sides, but it's painful because anything I say gets interpreted. Like I didn't say that. I didn't mean that. You're assuming I'm on that side and assuming this whole background. I'm not saying all that stuff. Anyway, I think that we need more love for each other, to see each other for who we are and not. Okay. That's a separate podcast. But one thing that I think that it's very useful to see is that where does closure shine and where does Haskell shine? They're both really well-designed languages. And they have this major difference of static versus dynamic typing. Structure shines really well when you're dealing with unknown and changing data, right? There are some JSON APIs that I've used that the type, if I was going to type that JSON, meaning develop a type that fully represented all the possible responses I could get back. It would be a gigantic, unununderstandable type. I would not be able to understand the type two weeks later, if I wrote it, if I got it right. What closure says is, well, maybe you don't need to understand the type that well. There's parts that you can ignore. You can handle different parts in different branches. So I'm thinking, for instance, of the WordPress API, sometimes it will return, let's say it's returning a collection of something, like a collection of blog posts because it's WordPress, right? Sometimes it will return an empty array, sometimes it will return no, and sometimes it will return false for zero or something like that, and you're like, why don't you just return an empty array, like it's either an array, it's either got zero or more, you know, like that seems to be like the type, right? But it doesn't do that. It returns false sometimes. And sometimes the type of a field depends on the value of some other field. This is very hard to type, and it's hard to, like, you read the docs and that's not in there. You have to actually make the request and see what you get and develop it kind of incrementally. Like, oh, okay, sometimes I'm going to get a zero, so I got to check for that. So the closure is better at that, like that incremental, like, sometimes I'm going to be strict on types, but only in certain branches and, like, so you can be very flexible. Haskell is good for when you have figured out your domain, and you want to tighten it down, and, like, you know exactly how it's supposed to work, and you want to avoid all the corner cases and you want to encode that as a check very early in the compile stage, right, and you want the logic, you can figure out all this logic about it and encode that as a type. And I feel like what we need is the two extremes. We don't need a, we don't need, so what a lot of people try to do is come up with, like, a compromise in the middle, right, where you have, like, oh, let's, you can do what you do in closure, but we're going to kind of check it for big errors and big problems. Like, that's not so useful, you know, for instance, like, something might return a null and you didn't check that, right? Like, okay, that's useful, but, like, is it worth the whole complexity of this type system to just know that? What you want is the two extremes. The, I'm going to not check the types, and I'm going to be able to be very dynamic and go down different branches and then kind of at runtime, make stuff make sense, right? Convert zeros to empty lists, and, you know, all that stuff, let me do that here. And then there are some things, even in closure, where I'm like, I know how this is supposed to work, and this needs to work right every time, and I want as much checks, and I don't want anybody to change it, I want to lock it down with a type, I want to, I want to never write, be able to write code that will break it, like, all these things, I want that too. That's the other extreme. And I don't think you can, you don't want to compromise, you want the two extremes. And so anyway, that's my idea, is that this is why TypeScript works, it's because it's got the two extremes, it's like, you've got the, it's totally loose JavaScript, do whatever, and then you've got the, I only accept these five strings to this function, right? And tell me when I'm doing something different, like, I could, you could make your build system fail if, like, the TypeScript doesn't check, and you want to move kind of discreetly from one to the other. You want to say, okay, there's a totally loose function and there's now, okay, I know the five strings it should take, I'm going to encode that into TypeScript, and, like, lock that down forever, I mean, you know, for the foreseeable future, let's say, and that, you know, that's what you want. And I think Haskell, it's hard, but what it needs to do is let things be loose, like really loose, for certain classes of problems, like those kinds of, like, loose JSON APIs. You know, I've, I've fielded a lot of complaints from Haskellers, like, you know, arguments, let's call them, where they'll say, like, well, how do you use an API if you don't know anything about it? It's like, no, that's not the, that's not the problem. You know a lot about it. It's just that it's really hard to take all that you know about this JSON API, and encode it as a type. It's really hard to do. It's got too many variables in it. And it's not about zero knowledge versus total knowledge. There's knowledge that doesn't fit the system. Like the system was not designed for this kind of Lucy, you know, it's PHP people. They're returning whatever, right? This is another dynamic language generating the API. It's got multiple. It's an open source project. You know, there's, you know, hundreds, thousands of contributors changes to it. It's got backwards compatibility things, like they don't want to change existing stuff. They made mistakes. They know it, but they have to keep it there because there's clients, like there's all these issues, it's evolving over time. So like your type that you wrote today might not work tomorrow, right? And you know, these are just the realities of working with these kinds of systems. I believe it's the kind of thing you'd never be able to lock down with a good type. I've heard the same thing with people dealing with like medical records. Like there are standards, but then the standards are evolving all the time. And even with the standards, they're not. They don't capture exactly what the doctor wants to say. And so the doctor will take a field that's supposed to be a number and they'll write NA, not applicable, right? That wasn't captured in the standard that they should be able to do that, but they needed to do it for that particular patient. And so now you get the CSV of all these medical record things. And it says it's supposed to be a number. We typed it this way. And now there's an N name. What do you do? What do you do? And in, you know, it depends on your application, but in closure, we say, well, let's not try to parse it yet. Like maybe we don't even need that field for what we're like this particular task that we're doing. We don't even look at that field. So why even try to parse it as an integer? Just let it be. Just let it be, right? Or like maybe some point down the road, something will know what to do with it, right? So we got to leave it because they might convert the NA to zero or some other default that really depends really on some other information that we'll have at a different stage. So we have a model that's loose for a reason for dealing with these systems that aren't giving us like perfect information and it's changing and stuff like that. But there are a lot of systems where you know it. You know the model and you want to bake it. So that's my, you want the extremes. Yeah. I really like that explanation. But I also really liked your message about like let's learn from each other like let's learn from different programming languages because I think like there, this is what I really liked about your book that you said like even if you are in JavaScript, you can still apply ideas from here because the ideas are not bound to a language, right? They are ideas and we can apply them and in some language it's simpler and in other language it's more complicated to use them. But we can use the ideas and I think that our programming community is very split into camps and people tend to ignore what other camps have already learned and then relearn it the hard way right instead of just learning from them what they learned, right? And there's no right solution for everything but I think that it's worth listening to what other people have learned in their field, in their language, in their framework or whatever and this is why I really enjoy conversations with people that are doing other programming languages, other fields because I always learn something that I can apply in the languages I use or the frameworks I use. Yeah, I think we do need, we just need to be patient with each other, we need to listen to what the person is saying and not assume, it's just like in politics, like a person makes an argument about some issue. Like listen to what they said, don't assume, oh they are on this side of that issue and so I'm going to put them into this camp and they must also believe this and that and that. They didn't say those things, just listen to what they say and I know it's hard because there's so much history of debate and bad arguments, bad rhetoric about it, misunderstandings that have been voiced and now you resent the other side for misunderstanding you so you're going to not listen to them and we've got to have some love, we've got to come together. I actually should do like a podcast by that because I feel it's very emotionally, I feel un-listened to and I know I've made mistakes and I haven't listened to people and I'm trying to listen and it's not easy, yes and the idea of functional programming being something that you can take skills from even if you're not in a functional language, I think that this is another thing that the community, the functional programming community has been poor at communicating, that we often look at the most advanced stuff, like Haskell and some really far out idea that using an effects system with applicative functors and all this stuff, we see that as the answer and anything less than that is just hacking and saying that in an insulting way, I think that that undercuts all of the great skills that are fundamental to those ideas that, you know, let me put it this way, I've been teaching closure for a while and I would teach all these cool, far out ideas thinking, yeah, this is where it's at, this is where like, man, people are going to love this and then I'll get a question and someone's like, oh, I'm having trouble with my code, it seems like I'm not really getting how to do this enclosure and I'm already to apply some like, new cool thing and I open up their code and I realize, oh, you are using global mutable variables, you're not using map filter and reduce, it's just like really basic stuff that you need and I read a book that was addressed to the PHP community, it was basically how to use map filter and reduce in PHP and it was a huge success because no one had written that for them, no one had said like, hey, there's a lot of benefit just from this really basic idea and so that was a eye opener for me for when I was writing Grocking Simplicity was like, no one has addressed these really basic things, like, how do you recognize if a function has side effects and what do you do? Like what is a mitigation that you can do if you have these functional side effects, how do you start to make, you know, pure functions, functions without side effects from them? What parts are good to do that way, why would you do that? Like I spend eight chapters on this, on pure functions, whereas most functional programming books will define it in a sentence or two at the beginning and then go on to monads, like, you know, right away, it's like, whoa, there's a lot there that like, no wonder it seems inaccessible to people, it's like, we're not, no one is writing the basics. Yeah, true, but I also think that there is something that you need before you go into that and I think that's that you don't order programming languages or paradigms in like a hierarchy, like, oh, oh, and if you get better, then you go to FP or PHP is worse than all other languages, right? I think there are definitely things that other programming languages, programming language communities can learn from PHP and a lot of people don't because they think, like, oh, PHP, what can I even learn from those people, right? And I think that's like a huge mistake we're doing. That's why I think that things that we, like, I think that the things you wrote in the book, they are very basic for you, right? But they might not be basic for other people, but you're not writing it in a style that says, like, you should already know that, why don't you know that, right? And I think that's valuable and I think that's something that we all should do more and teach people things that we think are basic for us without assuming that they are less smart because they don't know that, right? Right, right. Right, right. I definitely, I mean, I definitely believe all of that and I tried to put that in the groggings simplicity, like, I treat functional programming as a set of skills, right? So it's a set of skills that might be foreign to you if you don't know functional programming and you might know some of them, but it's, you know, it's a loose definition. It's skills that I see functional programmers do more than non-functional programs, you know? So it's like a very loose definition, but these are all skills that, I mean, every functional programmer I've talked to has said, "Oh yeah, these are important, I do these every day." And so it's interesting to me that no one had defined functional programming in terms of these particular skills and I've had debates, I was having one yesterday about what is functional programming and like it's my, the stuff I'm saying about actions, calculations and data, is that the definition of functional programming? They're saying no and I'm saying, well, maybe it's a good definition, maybe it's not. It's a working definition, it's the definition for the book and regardless, if you ask any functional programmer, is this important to know for a functional program? They would all say yes, there's no doubt. So, you know, kind of inverting the question, it's like, let's not try to define it, let's just say, what do all functional programmers know and let's start there. That seems to really let the ideas bloom into the book. And I also think that it's maybe it's also not very useful to define functional programming, right? Maybe it's more useful to just say here are a lot of ideas and tools that you can use and you can use some of them, maybe someone reads your book and all they take away from is structural sharing, for example, right? And they add it because it solves their problem or maybe defensive copying is solving the problem for them because they have a legacy system which does weird stuff and it helps them protect it, the application from that, right? And I think that's much more useful to see like the ideas and maybe a combination of those ideas is what helps you. Maybe one of those ideas is not helpful to you and you ignore it, right? And I think that's really nice. The all or nothing approach, I think, is too much to ask. It might be a useful ideal to move toward. If you are dedicated to functional programming and you want your system to have all the benefits of it, like have this ideal in the future, like we're going to have no side effects and it's going to be like that's a useful ideal to move toward. But you got to start where people are and I think the movement toward the ideal is what I'm calling functional programming. Not the ideal itself. You'll never get there, right? The arrival is functional, like that's not a good definition. Yeah. So thank you so much for writing the book, I really enjoyed it and thank you so much for this conversation, we have a few e-book codes for people that want to read your book and for the others links to the book. So if you listen to the episode and you find one of those codes and they still work, then you get the book for free, otherwise you can check it out. So yeah, thank you so much for your time and to everyone else, have a nice day. Thank you so much, it was a pleasure. (Music) (Music)