Theory of Functional Programming
In this talk, I explain functional programming as discussed in my book Grokking Simplicity. Presented at Git Commit Show 2020.
Transcript
Yes, this is going to be pretty fast, so I'm going to try to get started. My name is Eric Normand, and I'm going to be talking about a theory of functional programming. Let me share my screen. So, the theme of this conference is mastery, and I'm writing this book on functional programming called "Grocking Simplicity." It's out now, but it's not finished, it's in early access, and in the book, I'm really trying to teach people to use functional programming in a practical way, and so one of the questions that I have to address in this book is, "What is functional programming?" It's not easy to define, and often when we are presented with a choice of technologies, we think, "Okay, I can choose functional programming," or "I can choose object-oriented programming," but as you become a master of programming, it really is more like this. You want to be able to look above the two things, the two paradigms, and choose pieces and parts to better fit your problem, and so functional programming and object-oriented program can coexist, and you have to master both so that you can choose what to do. But then, of course, you have other choices, and so you just are choosing all sorts of stuff from what's going on, and so when I write the book, I'm trying to think of functional programming, not as a set of features in a programming language, not as a fixed set of prescriptions or prohibitions, but a set of skills. These are skills that you can apply in any language, any context, and so that's how I'm going to try to approach it today. Now, I can't go over everything in the book in one talk, so I've selected little pieces. The book is actually organized already in terms of three levels, so the first level is foundational. This is the main definition of functional programming that I use, so you're learning to distinguish these three categories, actions, calculations, and data. We're going to get into that more deeply. Then, the second level, you're starting to use first-class functions, higher-order functions, stuff like map, filter, and reduce. We'll get into that, and then the third one is all about data modeling, so there's these levels that progress. The thing is, the first one is the most important. The first one is going to give you the most benefit, and they get more interesting, but also they're more like icing on the cake as you get better and better. Let's go into this first part, this level one. There's really two things that we're going to learn in this talk. The book goes into many more things, but we're going to talk about what are actions, calculations, and data, and see how actions spread throughout our code. In terms of mastery, the big ideas here are that as a master, you're trying to make important distinctions. As you get more and more masterful, you're able to make finer and finer distinctions that are important, and ignoring details that aren't important. You're also becoming a master of your own attention, where are you looking? You can identify problem areas faster because you're able to direct your attention at the important stuff, and you're also able to anticipate consequences before they happen. Let's get into this. Let's say we have some code, it's not really important to understand all of the code here, but these are just little snippets of code. In functional programming, we start to subdivide these. The first and most important distinction we can make is that some of these pieces of code depend on when they are called. These are called actions. If I send an email, that depends on when it's called. If I send an email today, it's different from sending it in six weeks, also saving the user to the database. If I send it now, if I save it now, it's going to be different from saving it later, it could overwrite something that was already saved, et cetera, get current time. Every time you run it, it's going to give you a different time. The other stuff below the line does not depend on when it is called. We can further subdivide those things because there are things that don't depend on when they are called that run, so that there are code that runs, like a sum that does a calculation, does some kind of computation, string length also computes something, and then we have data. This stuff is inert. It's just facts. I have the number 10. It can't run. It's not going to have undefined behavior. Let's go over actions, just a little review. The definition of action is the process of doing something typically to achieve an aim. This is having some kind of effect on the outside world, the world outside of your software, sending an email, saving to a database. Usually when people talk about this, they'll call it effects or side effects. I don't like those terms, so I've used the term action, but you'll see that a lot. We obviously have to understand those terms. They depend on when you run them or how many times you run them. Just as an example, the email example, if you send it zero times, it's different from sending it one time and it's different from sending it two times. It depends on how many times and then when it is run. If I send the email today, it's different from tomorrow. Calculations, these are computations from inputs to outputs. They always give you the same answer. These are also typically known as pure functions. If you want, actions would be the opposite, which is impure functions. They are eternal. They're outside of time. They always give you the same answer. Another term that's often used is they are referentially transparent, meaning they can be replaced by their output and it wouldn't make a difference. Basically, it means you could even run it zero times, like your compiler could totally eliminate the code and replace it with the answer. Finally, the third category is data. These are facts about events used as a basis for reasoning, discussion, or calculation. This is probably the easiest one to understand intuitively because we all get it. It's inert. It doesn't run. It's just data. You realize it easily. The interesting thing about data is that it requires interpretation. You need a program to run over it, to have it do anything, to make it meaningful. Otherwise, it's just bytes, it doesn't mean anything. The flip side of that is that you can interpret it in multiple ways, which is really nice. Okay, so this is all about the spreading rule. Here we have Jenna over on the right and she's got some code, these three functions. Maine is at the bottom, and Maine calls affiliate payout. Affiliate payout calls figure payout and figure payouts at the top. The highlighted code here in figure payout is called send payout. What this does is it sends a message to the bank, to the bank's API, to transfer money into somebody's account. She is claiming this highlighted line is the only action. There's only one line that's an action. This is pretty functional. Well, she's not aware that there is a thing called the spreading rule. But the spreading rule, well, we'll just apply it. Here is the action. By the spreading rule, because there's an action in this function, figure payout, the whole function is actually an action, because it depends on whether you call it or not zero or one times. If you don't call it, the money doesn't get transferred, or if you call it twice, the money gets transferred twice. Since figure payout isn't an action, then we have to highlight its call in the affiliate payout function in the middle. Since that one calls an action, the whole function is an action. This is the spreading rule. Since affiliate payout is an action, that means we have to highlight affiliate payout in the Maine function. Again, since affiliate payout is an action and Maine calls it, Maine is an action. Basically, everything in her code is an action by the spreading rule. In functional programming, what we do is try to not have actions at the bottom of the call graph. There's going to be some last action, and then after that, it's all on the call stack. After that, it's all calculations. The question is, why? Why is this important? Well, calculations and data are easy. It doesn't matter when they run. They're easy to test, because they can run on a different server. They can run before. You can test them many times. Remember, actions depend on how many times they're called, but calculations don't. You can run it 1,000 times to test it. Actions are just inherently harder. This is the primary distinction that functional programmers have to make, because they want to pay more attention to the actions. Calculations are so easy you can have a machine check them. Actions are what are really difficult and where most of your bugs are going to find. This is really where the mastery aspect comes in. You can direct your attention to the most troublesome problematic parts of your code. In Level 1, we learn to manipulate these things, separate out calculations from actions in our code using the factoring, but we're going to proceed on to Level 2. This is where we use first class functions and higher order functions. This is all about solving problems, separating concerns. In this talk, we're going to talk about these three things, about first class values and how they help you abstract things, how they can first class functions can represent behavior that is a parameter to something else, and that gives you higher order functions to let us separate concerns. In terms of mastery, this is where you're getting finer grain control, because a first class value is something that you can use your whole language to manipulate. Let's you think more abstractly, you're thinking generalizations instead of specifics, and then you're getting, by separating out these concerns, you're getting a much higher resolution perception on what's going on. Let's look at a code smell. This one is called implicit argument and function name. So I have this function here at the top. It's called set price by name, and it takes a shopping cart, and it looks up an item in the shopping cart by the name, and then it changes the price, right? It sets the price based on the argument. And then there's two similar functions called set quantity by name and set discount by name, and the code looks almost identical. The only difference is that they don't set the price. They set the quantity and the discount. So notice that here at the highlighted yellow stuff at the top, the name of the field appears in the name of the function. So it's kind of like this implicit argument that the field name that we're using in the object set is actually named by the function itself. So we're going to do this refactoring that's taking this implicit argument that just is part of the function name and making it an explicit argument. And so this code at the top becomes the code at the bottom that we call it set field by name because now it's generic, and we're adding this argument called field. And that's what gets passed into object set. So instead of having a set price by name and a set quantity by name and a set discount by name, you just have this one function called set field by name. Okay, and this is a way of making field a first class value. As part of the function name, it's not first class. You can't do anything with it. But now that it's just a string, now you have your whole language to help you manipulate it, all right, next refactoring is called replace body with callback. This is sort of the next level that was a that was data that was just a string that we're passing in. Now we're going to learn how to pass in a function. So we have these two for loops. And they're doing two different things. The first one is cooking and eating the food. Okay, you have an array of foods and you loop through and you cook them and eat them. In the bottom, we have an array of dishes, so you have all your dishes when you're done. You got to wash them, dry them and put them away. Okay, it's a silly example, but it's good for a slide. But notice that they have a lot in common. This red part at the top is very much like the red part in the second for loop, right? You're initializing a variable at a zero, you're comparing to the length of an array and you're iterating. The stuff that's different, oh, and even the ending brace, you know, this closing brace at the end is the same. What's different is what goes inside the loop. So how do you remove the duplication of this, this code? Because you can't separate the for loop from this closing brace. So this refactoring lets you do that. It lets you replace the body, which is this green part, with a function, and then that function gets passed to another function, right? So this F becomes the body and you're just going to call it with the current element that you're looping through. Oh, that should be a array dot length. I'm sorry. That's a typo. So you're going to make a new function that takes the array and the function that represents the body and then you can loop through. So let's look at what this looks like when we apply it. So on the left, we have the original code with this duplication and stuff. And after we have this new function called for each, and then we can apply for each to the foods and this function that takes a food and it cooks it and eats it. I put the space just so you can see that it lines up here. And then likewise you have the for each over the dishes and it does the same thing as our original for loop. But I've highlighted, I've underlined the stuff that we don't have to worry about anymore. We don't have to notice in this code, we don't have to initialize any variables. We don't have to check the links. We don't have to increment our eye. And we don't even have to pull out the current food from the array. That's all done for us. Okay. So we're now able to much more clearly and directly express our intention. You don't have to worry about these little variables. Okay. So now let's move on to level three. Sorry, it's so fast, but I got to keep going. Right. So this is all about building powerful data models. Like I said before, the most important stuff is in level one, it's the fundamentals, always the most important. And as we get more and more skilled, we'll be able to use, we'll master those things. We won't be thinking about levels one and two anymore. We'll be more focused on shaping our data so that it gives us these long-reaching consequences of that are really hard to talk about, almost mystical. And so that's why this is going to be a little bit harder to talk about, but we're going to go there. So we're going to be modeling facts with general purpose data. Okay. And this gives us models with very high fidelity, meaning we're taking some real world thing and we're going to be able to translate it into a data representation that captures all the important stuff. Very high fidelity. Okay. And then the next thing is we're going to be able to define operations on this data that lets us use something called the closure property, that lets us express way more than we could with a regular API ever could. So let's get into that. So here's an example. At the top right, there is a pepperoni pizza recipe, standard kind of recipe, you'd see in a cookbook. It's got the ingredients, tells you how to prepare the ingredients and then how to assemble them. What we've done is we've translated this on the left into JSON and we've used the properties of JSON to model this recipe as high fidelity as possible. So we're keeping the same structure as in the recipe. So for instance, we have the name up at the top. Okay. So we'll have the name and then there's this section called ingredients. So because the ingredients is a section, we're going to make it a sub map indexed by the ingredients name and we're doing the same for preparation and assembly. Now ingredients, the order that the ingredients show up in is not important, right? So we've kind of, we put it in a map because it doesn't matter what order they're in. And then we are also using the quantity as the value in the map. So five tomatoes becomes tomatoes, the key with five, right? So that way we can look up at any point. How many tomatoes do I need? How much cheese do I need? Okay. Now the preparation, this one also doesn't, the order doesn't matter in this particular case with pizza because preparing the dough is entirely separate from grading the cheese. You don't have to do the dough first and then grade the cheese second. You can do it in any order, you can do them even at the same time if you've got a helper. And likewise with all of these other ingredients. So that way, that's why they're not in an array that maintains order. Okay. And then finally, the assembly does have an order. So we've put them in an array so that they maintain the order. Okay. This is something that we kind of do all the time if we're working in JavaScript or some other language with a lot of data oriented programming like JSON. But in the some languages, we would use a class and we would represent this preparation as say a method, right? And there would be some like pizza interface and then we'd have specific classes that implemented that interface and the preparation would be represented as a method, which is code. Not data. It's a calculation or it's an action and it would have there would be totally opaque. The only thing you could really do to it is run the method. Well, this way we're able to analyze it. We can count how many things we have to prepare. We can calculate how much time it's going to take, et cetera, because it's data. Okay. And so the next thing we want to learn about this is that we can actually take. So let me go back, see this ingredient list is a map from ingredient name to quantity. Okay. So we have this this this sub map is what we're going to focus on. So I can actually make an operation called ingredient list plus that takes two ingredient lists and adds them together. This is the result of this, these two being added together. One pepperoni, two cheese plus two cheese and three flour, I've, of course, omitted the units, but you get the idea the so we're going to add the cheeses that gives us four, but then the pepperoni is only in one list and the flowers and the other. So they just come in on so we can all imagine how we would write this. We could even imagine writing a bunch of these kinds of operations. You got your plus, your minus, your time, so you could say, I want to make three pizzas, how much cheese do I need, right? Divide, you know, I want to make half a pizza or split will give me the flour in one, in one list and everything else in another list. But there's something really important about these operations is that they take ingredient lists and they return ingredient lists, they operate on ingredient lists and they generate an ingredient list. That's called the closure property. And this is something very subtle and special because what it lets us do is it lets us nest our calculations, okay, so I can, let's say I want to, someone comes in with an order, you know, I'm working at this pizza restaurant and they want four cheese pizzas and three mushroom pizzas. So I get the ingredients for the cheese pizza from the recipe and I get the mushroom ingredients from the recipe and I multiply the cheese by four and the mushroom by three and then I add them together. Now I know all the ingredients I need to fulfill this order, okay, because times returns an ingredient list, I can then pass it to another level of expression, right? Likewise if I wanted to, I'm going shopping and I know that I'm going to need a cheese pizza and a mushroom pizza but I need to go buy some flour, I want to know how much flour I'm going to need for those two pizzas. So I'm going to add the two ingredients together and then I'll split on flour and that'll give me both the flour ingredient list and everything else in a separate one. And this property, you know, you can imagine making arbitrarily complex expressions using these like formulas and stuff, using these operations, just like you can imagine an infinite number of expressions using just plus and times that you can put parentheses and nest them and you can make an infinite number. Well now we can make an infinite number of these and precisely target any question or calculation we have in mind for ingredient lists. And that's it. So that's a thing to target when you're building out these APIs is this ability to make infinite number of expressions. All right, thank you, that's the end of my talk. And now I'll open it up for questions. So Pradeep, did we get any good questions? Great, thanks a lot for this presentation, Nadek. So it's a wonderful talk and specifically how simple it was, you broke down the complex idea into simple and converted them into a simple function, simple types and I got to learn about the different things like action, calculation and data and then separations of concerns and all these things. So overall, I learned, I think my programming is going to improve with this talk. And meanwhile to audience, you can ask the questions in the chat, you can, and we will ask the questions to Eric Newton. Okay, so till then let me ask the questions. So this is a very generic, I will say, generic talk, not concerning any programming, not concerning any kind of tools that you can utilize somewhere anywhere. What, how do you, how do you get people to start working on these ideas? What should be the starting point? I mean, yes, I learned about the things, but let's say I have a project already working and then how should I go about it, refactoring and all these things? Right, so my big thing is that the most important thing before jumping into your code and changing stuff is just to be aware, just to be aware of this idea that some code really does depend on when it's run, and you want to be careful about when that happens. And some code, you can run it a million times and, you know, sure, it'll use CPU processing, but it's not going to hurt anything by being run the wrong number of times, for instance. So those are your calculations. So just being aware that, hey, this, the reason this is really, you know, always getting bugs, bug reports, this piece of code is because it's, it's an action. It's hard to test. It requires a lot of care when you use it, and it's hard to enforce that. And just that awareness of the fact that it's bound up in time is really going to, going to change things. Now there's refactorings you can do to kind of separate out what, what doesn't have to be an action, right? So, you know, sending the email, yes, that's going to be an action, but you can even figure out what the contents of the email are going to be in a totally as a calculation before you send it, right? This is the, this is the big distinction that we're trying to make, like as little code as you can in actions means more code and calculations, figuring out what are the contents of the email, and then further, well, if I have to send a thousand emails or a million emails to my customers, I could even generate all of them as calculations, the whole list of a million emails as calculation instead, and then run through it when a really small tight for loop, as opposed to the for loop contains all the logic of sending and determining who's going to get it and what emails they're going to get. So just pulling all that stuff out, this is something that's much easier to test. If you could put them all into an array and you can all, you can now say, well, how many are in there? Is that correct? You can test it before I start sending it. Got it. One more question I have is about a function that you showed, said price by name or said feel by name. So you, so you said that instead of implicit argument, you change them to a P explicit, right? Yeah. So there could be another, I will say another point, a thought process that setting said, a set by a price, this makes the code much more readable that just by knowing the function name, people know that what this function is going to do, right? But when you create configuration, so this, I'm just saying it kind of configuration because you are passing it as a parameter and probably you can have a multi more version of the same thing, right? So do you see this as a complexity being resolved or increased? So this is a really good question. And I think it gets at the heart of like API modeling and what's the purpose of the code. If, and so there's no clear answer like in a generic sense, like you should do this and not that, right? It's definitely going to be contextual like if, if you're making an API and you're finding that the clients of my API are constantly asking, oh, can we have one that sets price? Oh, can we have one that sets quantity? Oh, wait, we need one to set the name and oh, we need one. You know, you might say, hey, wait a second, like the whole point of this API was so that you could work without talking to us. We can write code all day, right? We could write your code for you would be faster. You're sending us three emails per method that we have to write. So if you're finding that the API is not serving that barrier that is not saving work, you might want to consider saying, hey, these are the 12 fields that you can set, pass them in as a string and you, there's only one function that you have to write. And we're, this is all the stuff that's there. There's no other way otherwise every time we add something, we're going to have to publish a new method, publish a new, you know, documentation, explain how it works when we could, and they're all going to be the same and we know it. So why not just give you the list of strings that you can pass in, or an enum, or, you know, something like that, depending on the language you're using. However, I do see that there's times when, hey, there's only three things we want them to be able to set. Why don't we just be explicit and make up a method for each one? And those are never going to change. And this is going to be the clearest way to do it. That's, that's great too. It depends on who's going to be using this API and, and how big it's growing. We got it. So how does a functional programming, what do you mean by functional programming here with this? How does it connect with the functional programming? Is this what you're suggesting? Is it what we are calling functional programming? Right. So I'm, I can't, that's a good question. I came up with, so if you search for the term functional programming, there's a lot of vague definitions, the clearer ones are more academic. They are, they are reductionists. They'll say something like functional programming is programming with pure mathematical functions, the end, like end of discussion. And I am a functional programmer. I know a lot of functional programmers in industry, I'm not in academia, right? I've worked for a company and we, we use functional programming. I mean, at least we would say it's functional programming. We read the papers from academia, you know, we're into that. But these, this definition does not speak to us. It does not speak to what we do day to day in our work. We don't just use pure mathematical functions. We'd have mutable data. We talk to databases. We talk to APIs. We have GUIs that we modify, you know, their state in our application. And so I was looking for a definition that was more practical that still maintains the, the truth of that academic definition. So there's something to that, that this focus on pure functions is the, is the main thing. It's just not that like end of story, you know, full stop. We don't want to do that. We want to say, well, wait, wait, wait, we use actions, but we use them in a certain way. And we use calculations, pure functions, but we use them in a certain way. And so making that distinction that actions are part of it, pure functions are part of it. Data is part of it. And making that like, first distinction is the entry into functional programming. So I'm hoping that this is a much more practical definition that, you know, it's their definition, good academic definition is practical for academia because they're trying to publish papers. Right. And that's, that's their, their goal. And this kind of reductionist definition lets them explore like, what can we do with only pure functions? And that's not what we're trying to do as, as industrial programmers. We're trying to get work done, but without bugs, you know, as few bugs as possible. And so we are using the same idea like, well, what, what is the hardest part of the code? It's the actions. So what, how can we manage those better? So that's, that's how I, that's how I've broken it up. Got it. So when talking about callbacks and talking about practicality, I think there, there are many things that we need to think of or need to integrate with, let's call them our dependencies. So how can we maintain simplicity as well as being practical when dealing with other dependencies. So such as we talk about promises, it does add some kind of complexity to the code, but at the same time, it also solves some other problems. So how, what kind of, do you share any tapes or mental models, how to go think about the dependencies? Yeah. It's a, it's a good question. There's a lot to say, but you mentioned mental models. And so I think that one thing that's really useful to do is to think about where your complexity is coming from. So there's, I'd like to think of there's three main sources of complexity. One is your domain. So if you're programming a bank, a bank has a certain amount of complexity, right? There's just a lot of concepts, things that, you know, rules that you have to follow in the bank, it just comes with its own stuff. And there's no way around that. If you're going to make a bank that's legal, you know, that's doing the right laws and calculating accounting correctly, you're just going to have to deal with that complexity. Then there's complexity with the architecture. So you mentioned promises. If you are in a JavaScript and you're doing promises, there's a certain amount of complexity that just comes with JavaScript, right? Same with Java. It has its own complexity that you're just going to have to deal with. So when you're asking is, you know, promises have a certain amount of complexity, it's, they are actually a good tool for dealing with the preexisting complexity of JavaScript, which is that you have callbacks, right? This gives you a little bit more power to handle callbacks better. Then the third source of complexity is your actual code, right? So you made a choice to use a mutable data structure, right? And so now you've got to, you've got to manage how this thing is being shared, how it's being mutated. That is a choice that you made as the programmer. And you could have made a different choice. You could have used an immutable data structure. Now in terms of dependencies, you know, I'm, I'm thinking like you mean if you have a library and you're saying, oh, I want to use functional programming techniques, but this library does not, right? So how do I work with it? That is a really complex question. You know, it kind of depends on what the library is doing. But one example I can give is if you want to maintain an immutability discipline, meaning you make an array, once it's made, you don't, you don't change it, right? You just leave it and you make a copy if you want to make a change. But you need to pass this array to this code that you know is going to modify it, right? How do you work with that? Well, you make a copy and you pass it to the, to the thing that's going to modify it. So you're to the library. And then when you get the answer back, which might be inside, written inside the array, you make another copy and you say, okay, that I'm throwing that one away because I don't know what it did with it. It might change it later. You know, it might reuse it. So I'm going to make a copy right now. And then I won't change it, right? So you can maintain a barrier where inside the zone, inside the barrier, you feel like you have maintained the discipline and you can still use those external dependencies and they don't have to have that same discipline as you. Does that make sense? Does that answer your question? Yes. Kind of. So yes, but I have a more follow-up questions. Yeah, sure. So it does that dependency will add complexity and we can be more mindful about it. Like how we can reduce the complexity about immutability, I think talking about functional programming and immutability. That's a very deep talking, but I will just, can you just talk a little bit more about it? How can we do that? Should we use immutable JS or these libraries or how to go about immutability? That is a good question. So in the book, we don't use any external dependencies. So it's all the examples are in JavaScript, but they're meant to be just readable by anybody because JavaScript is kind of universally readable. And so we do our own immutability. We don't use like immutable JS. I know people who like immutable JS that use it. I myself find that it doesn't give me the power that I want. The API is not right for what I want. And so I would rather use something like low dash that does everything it does makes copies. So if you're doing map, low dash map, it makes a new array, it doesn't change the existing array. If you're doing filter, it makes a new array, it doesn't remove elements from the array, it makes a new one with the elements removed. And I find that that's good enough, you know, that just making copies when you need to modify something. It's fast enough and you don't need a special like data structure that prevents the copying or prevents the modification. God, so what you mean is it is practical to either do your work on your own way for immutability or load as you will consider as an alternative to make these things. I would use low dash. It's always better. These tools have a lot of users and they find the bugs and, you know, they fix them and then they do speed tests so they know the fastest way to implement it way faster than you could do, you know, the first time. I do have to say that I would rather have immutable data structures, but they have to be the default in the language. So I use closure a lot and they are default. And what that means is all the libraries use them to. And what I found with immutable JS was that sure, I would use an immutable array or an immutable hash map, but I can't pass it to any library. So then I'd have to make a copy anyway into a JavaScript standard JavaScript array or standard JavaScript object and pass it to that. And then when it came out, I had to make a copy again back into a mutable JS. I just found that that wasn't as nice as just using the regular JavaScript stuff because then I have nice literal syntax and low dash does the right thing. So if I'm just using low dash, I'm fine. And then, you know, sometimes you do need some optimization or speed I could modify an array if I needed to, you know, that happens as well. Amazing, closer. I love Richard, I think he is closer authors talk, simple made. Yeah. Yeah. Yeah. That's a great thought. Yes. You had one. So and thanks a lot. And this one, I think combining these two talks, this gives some practical view on how we can go about functional programming, how to make programs simpler and be a better programmer. So thanks a lot for joining in. And well, I hope you can share this presentation on our GitHub is so that everyone who is watching can get to see see that. Sure. Do you have any last message for the audience anything you want to share? The only thing is I don't think that there's an inherent conflict between functional programming and object-oriented programming. So if you're skeptical of either one, I typically find more people are skeptical of functional programming, because they're like, but I know options are in programming, why would I change? I think that I want to apologize because there hasn't been a lot written about like a more practical functional programming side. But do give it a shot. Do you see if it helps you write better code to combine the two amazing. Thanks again, Eric for joining us and sharing your knowledge. I would love to see you again. Yes, it was great to be here. How can people reach out? You can share that. Yeah. So if people want, I'm on Twitter at Eric Normand. I've also got my blog is lispcast.com. And you can also go to if you're interested in learning closure purely functional.tv. But if you just search my name Eric Normand, that all that stuff will come up in Google. Awesome. Great. Thanks a lot, Eric. Yeah. Thank you. Bye. [BLANK_AUDIO]