What is Functional Programming?
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
In this talk, Eric Normand describes his ongoing work to redefine functional programming. The current definition serves academia but leaves a lot to be desired for industry. Drawing on his experience programming functionally, working with hundreds of other programmers, and teaching functional programming to beginners, Eric Normand has synthesized a new definition that attempts to capture what is most important about functional programming.
**Eric Normand**: This is a talk I am preparing because I would love your feedback. I'm writing a book and I'm working on these ideas. The ideas are not done, but I want to prepare and present it to you so that we can talk about it. Come up, or afterwards we can talk about it.
I gave a talk like this about a year ago. The idea was much less fleshed out, but it was still enough to give a one-hour talk about. I feel like I'm getting somewhere with the ideas. Since then, I've also signed a book contract and started on the book.
These ideas are a little bit more developed, if you were at that talk. I don't even know if any of you were. I don't remember. Were you there Brian?
**Eric**: No. It was maybe November of last year. No. OK.
**Eric**: It was at LaunchPad. It was called, "A theory of Functional Programming." I was rehearsing because I was speaking at a conference about it. You were there? Awesome.
What is functional programming? It's a question that people ask all the time. The typical answer, this is the one you find on Wikipedia, is a reductionist answer. It's saying functional programming is programming with mathematical functions. Full stop. End of story. Any other definition, any nuances, are excluded.
It's a very academic definition. You can see why they do that. It helps them, the academicians that is, by reducing it down to this, they're opening up the field. Let's see what we can do with functions.
The problem is, as an industrial programmer who does functional programming, that is not what we do. We do side effects all the time. If you talk to any business person and you say, "Well, sending an email is a side effect, so we can't do that." They'd be like, "That's what we have to do. That's our whole business."
If you're telling me I can't do that...They look it up on Wikipedia and they're like, "We can't use functional programming because it's going to kill our business."
We need a better definition for the industry. Something that takes the stuff that functional programmers actually do when they're not doing research, when they are actually building systems that they have to run and have to do practical things in the world, and quantify it and turn it into something.
My big, hairy, audacious goal is to define the field. There's no good literature about industrial functional programming and I'd love to start that. I'd love to come up with the terms, and the concepts, and the frameworks within which everything else can blossom and bloom because I can't do it all myself, but I can give it a start.
Let's go over this outline. First, we need to talk about the problem with software and its complexity. You can read the slide. Then, we're going to start talking about mastering time, mastering space, and mastering architecture. It's mysterious right now, but I hope to make it more obvious when we get there.
At the end, we'll wrap it up with a nice model of what's going on.
You all can hear me OK? All right.
We write software and it sucks, right? It's always buggy. It takes longer than we think. It's always over budget. It's just hard to do.
One of the reasons is that it's complex. We're developing rocket science software. It's going to be hard. It's going to be complex. We're going to get it wrong a little bit. Then, there's all this other stuff out there.
That's rocket science on the left. It's complex. It's hard. Rocket science is a difficult subject. If you're going to write software about rocket science, we add more complexity to it. We have it running in a browser so it's making Ajax requests. We got the DOM. We got CSS. We got all this other stuff that we've added to rocket science that makes it more complex.
The stuff in rocket science, if you're going to write rocket science software, you need that. That's complexity that you have to have. There's no way around that. That's called essential complexity. It's essential to the domain you're writing. You're not a rocket scientist. You're not going to help clarify things with a better model. You're not that person.
All the stuff we add as programmers, all the complexity, is called accidental complexity. This all comes from a paper called "Out of the Tar Pit," which is a paper I recommend. It's actually really insightful. It makes this distinction between essential complexity and accidental complexity.
The problem that we face is that, typically, the accidental complexity totally dwarfs the essential complexity. We're writing rocket science software, but we spend all day trying to figure out why the button doesn't work, why the loading spinner stays on even after it's done. Why does that happen?
We're not even thinking about rocket science all the time. We're thinking about DOM, and vertical centering, and CSS, and we can't get a grip on it. The idea is that this is expensive. This part at the bottom, the green part, that's what we're trying to capture. We capture it, and then we add all this other stuff.
We'd like to go to this other graph. We can't reduce accidental complexity to zero. Once you're on the Web, it's like an operational concern. This runs in the browser. OK, great. You're going to have Ajax, you're going to have timeouts, you're going to have 404s, you're going to have all those problems. That's the red stuff that you can't get rid of, but at least you can minimize that.
Of course, complexity costs money. It means more time developing. It means more programmers, smarter programmers, higher-paid programmers, maybe more tools to help you. We want to avoid that.
Show of hands. Who has already heard of that — essential versus accidental complexity?
This is something that I'm adding to it, the sources of complexity. These are sources of complexity that I think that functional programming does a really good job of solving. They're these three — possible histories, code paths, and changes. We'll go through each of them.
This is where the mastering stuff comes in. Mastering time corresponds to possible histories, etc.
Mastering time. Here we have a diagram. Imagine time is going down. The start and end is at the bottom, start is at the top, end is at the bottom. We have two timelines. Both of these timelines, think of them like threads. They share this variable called "oven temp."
It's initialized to 100 up at the top. A and B both read, so they get the current value. Then A is going to multiply it by 10 and store it back in. B is going to add 10 and store it back in. What's going to happen? What's going to be the answer when they're both done running?
**Eric**: Raised condition. What Brian is saying is that we don't know. It's not even that there's two answers, there's actually six ways this could run, which can lead to four different answers. Depending on how they run, which one happens first, and then which one happens second. See the different results. You can do these yourself to check them.
We've actually got four different answers here. This is what I mean by the different histories. These are different ways that these two timelines can run. Imagine you merge them together, and how they merge together depends on their timing.
How are the threads running at that time? Was there something blocking this thread, or it just didn't get scheduled, or it took longer than it was supposed to? All of that chaos in your machine is going to lead to different times. You run at different times, you're going to get different answers different times.
This is complexity. This is hard. This is what makes bugs happen that you can't reproduce. "I saw it one time and now I can't get it back." It's because, maybe this one where A finishes completely all the way on the left on the top. That one is rare because it needs all of A to start and finish before B even starts.
That might be rare but it could happen. That's where you get 1010. It's the only one that gives you 1010.
Your current rate keeps running, but it's going to happen sometime in the future. When it comes back, you don't know.
Look at this code. I'm trying to simulate that same two timelines thing. You've got oven temperatures is initialized to 100 and they were going to do two Ajax requests. We're going to just get two numbers. It's this api.com/number. It just returns a number.
With the call back, we're going to either multiply it and store it back in, or add it and store back in. We don't know which one's going to come back first. Even though the multiplication one is written first in the code, we don't know how they're going to interleave. We have the same problem. We have six possible histories, four different answers.
Let's just exhaustively talk about where all these timelines come from. They give a better definition of timeline. If you have threads, two threads, you have two timelines. That's simple.
Multiple processes. You might be running processes that talk to each other. This could be your Web server is running on the same machine as your database. They're talking. Multiple machines. You got a Web server and a Web client. It's different machines across the world from each other. You got timelines now. The server is going and the client is going.
Then there's async operations, because now you're giving up control. You don't know when your thing is going to get called again. We have this problem. I think this is one of the reasons why functional programming is getting popular now is all these Web programmers are realizing this is hard.
We're dealing with relativity here. I click a button and I think the value is now X, and you click a button and you think the value is Y. You think you clicked it first. I think I clicked it first. Who's right? That's relativity. There is no right. You need someone in the middle to just say, "OK, stop fighting it's X. Y, you lost. Get over it."
The way I like to think about it, it's if we were smarter, this wouldn't be a problem. If we could keep six histories in our heads and see, like trace through all the timelines and the possible histories and figure out there's 4 different answers, that's OK, but we can't. We don't do that. We code and we don't even see it. We can't do it. We cannot possibly think of this.
We got different results. We can't even reproduce them sometimes. They're so rare, but once you put it in production and things start getting overloaded, you have millions of hits, they start to show up somehow.
Just to explain how bad this problem is, on the left here, that's the formula for calculating how many possible histories there are. T is the number of threads and A is the number of actions in each thread. This is a factorial. It grows combinatorially. This is if they have this.
It's just to simplify it if all the timelines have the same number of actions. It gets harder to calculate when you have different numbers.
Look at the graph. I just graphed it from 0 to 20. This is 20 actions in a timeline. This is just for two timelines. At 12 operations, it's already at a million possible histories. 12 operations, that's nothing. I mean, we write thousands of operations in a single thread. We have stuff going in loops. 12, like it's nothing. We already had a million.
There's no way you can understand what's happening, not without some help. That's where I'm trying to bring this idea that functional programming has tools for managing this stuff.
What's something we can do? Most functional programmers will recommend you just stop using actions, or at least, minimize them. You don't need to do that as a mutable value. Don't read that, it's mutable. Have it passed in and it'll be immutable. If we can eliminate an action, we just cross out that one, we go from three down to two. Notice, we go from 20 histories to 6, real easy.
Let's go back. If you get from 12 to 11, you're already at like 750,000. [laughs] You've reduced it quite a lot, but you see these jumps are big. If you can get down here, you're already in good shape. Where it looks flat, it's not flat if you zoom in, but it looks flat compared to the million.
Another thing we can do. If you can guarantee, just by looking at it and analyzing the code, that two timelines don't share anything — they don't talk to each other, they don't have any mutable state in common, they don't rely on something that the other one has done, anything like that, then you don't even have to analyze them together — all the histories are going to be the same.
You can just say it was just one history because they all look the same. It doesn't matter how they interleave because they don't share anything. That's another thing you can do. If you have a pure function that runs in a thread, that's actually guaranteed not to share anything. That's kind of another thing that functional programmers want you to do.
Here's a thing that is interesting that I think came out of the functional programming world. We have these two timelines, A and B. They have six actions each.
What we can do is draw this line here and just say, "Look, before you go on, either of you, whoever gets there first, just wait for the other one." You do this with your friends all the time. I'm going to start work on the dishes, you do the laundry. Whoever finishes first, you can just sit down and wait for the other person.
Then you're synchronized again. It doesn't matter who finishes first, who finishes last. You can't control that, but you can wait for the other guy. What this does is it puts a break. It says the stuff before is going to finish before all the stuff underneath is going to finish.
You're going to have multiple histories at the top. You're going to have multiple histories at the bottom. Instead of saying it's six actions that have 924 histories, you're now saying it's three actions and three actions. They're going to multiply. You're going to have 20 histories at the top and 20 histories at the bottom. That's going to give you 400 histories. You get that? 20 different ways...
It took me a lot to figure that out. I wanted to add them at first. I wanted to say, "Oh, it's 40," but it's not. It's 400. Now, who's done this before? Who cut two timelines like that?
**Eric**: What did you use, Minka? Do you remember how you did it?
You have 12 requests to make. You don't know what order they're going to come back in but you can wait for all of them. It just totally cuts the timeline. Might not be the most efficient because you could have started maybe without that last one. It's still, for a programmer to be able to say I'm not thinking about it, way easier. It's a good thing.
A similar situation here. This one's different. Notice how these actions are all adding? They're all doing the same operation. There's something we know about adding which is that the order doesn't matter. It doesn't matter what order those things happen in. You're adding, adding, adding each time. Then you wait.
Maybe this diagram should be different. Anyway, if you wait for all of them to be done, that's basically one history. It's always going to give you the same answer. That's another way to reduce the complexity. It's another thing that functional programmers talk about is that you can make your actions not depend on their order.
Last one, I think, for mastering time. At the top diagram, we have the shared variable X. We have the same situation where one of them is trying to add, one of them is trying to multiply. What happens is we have those six different histories.
If we put them in a transaction, if we put the read, and then the add and the read and the multiply — this is on the bottom diagram. If we put them in transactions, we're going to guarantee that one of them is going to happen first. You're not going to get...There's some inter-leavings that are now impossible.
You're limiting the number of histories. There's still two histories. A could go first or B could go first. They're going to give you different answers but you've eliminated two answers. It's a possible way to help eliminate these problems.
Here's a diagram. I've divided up the world, the functional programming, into actions, calculations, and data. Actions are things that depend on time, and ordering, and stuff. They lengthen a timeline. If you have a calculation, it does not lengthen the timeline. Calculation is something like running a pure function. Just like, "What is the length of this list?"
It doesn't matter what else is running at the same time, if it's an immutable list. I do have to say that. I have this dotted line. Outside of that dotted line, things lengthen the timeline. Inside, they don't lengthen. You can use as many of these as you want and you're not adding any histories. That's a good thing. We want to stay inside that circle.
Then manage the other ones that we have to deal with. We have to make that Ajax request to the server. We cannot eliminate that but we can manage it with transactions and stuff like that.
Mastering space. Here's the problem with space. This one I'm not that sure about. Hiss or something if I get it wrong. If I'm making a leap, throw out a question. I really want to know.
This is still another source of complexity. We have conditionals in our code. We need conditionals in our code to make it correct. If we're implementing something, we need them.
Every time you create a conditional, you have at least two branches. Even if it's just the if/then and you don't even have an else. You have the case where it runs the then and the case where it doesn't run the then. Every time you have an exception, it's running a different piece of code, so that's a conditional.
It's different branches, let's say. When you have branches, let's say you have a conditional here. Underneath that, you have another conditional, and underneath that, you have another conditional. You're adding ways for the code to run. It makes it harder to think what is going to run next. What is the next thing that's going to run?
It's always, "It depends." Well, if this is true, we're going to do this and this is true...It becomes harder to reason about. The more code paths there are, the harder it is to figure out is this going to calculate the right thing for me? Is it going to do the right behavior?
This is the leap. I feel it. I feel it experientially. I know that there's some truth in here, but then I don't feel I'm really getting my hands around it and explaining it well.
One way to deal with this source of complexity, to eliminate the accidental part...because we need branches. There's 12 different kinds of rocket fuel. You need 12 branches to deal with all the different fuels.
One way we deal with this is by modeling things as data. I say we, I mean functional programmers. We model the possible rocket fuel types with data. We'll have a data structure that has 12 cases in it. That exactly models the domain. If you had 13 cases, that's not quite right. We have 12 cases. It exactly models the domain.
That's what this diagram is. The ideal model is rocket science. You talk to an expert, he'll tell you, "Yes, 12 is right," and then you go and you code it up. You want an easy correspondence between the expert's knowledge and what you coded up. By doing it in data, data is much more limited than a full programming language.
A programming language is Turing complete. You can branch and loop all day long. You get a function. You don't know what it does. You have to read it to understand it. Data, if you have a literal representation, you just read it. You can print it out, give it to your friend. They can just say, "Oh, I see. I see what it is. It's 12 cases."
Let's talk about the ways that we can mismatch our data model. One way is you can have a convoluted mapping. You could have the two kinds of fuselage and you just map it. They have two in your data model and there's two in the ideal model but you just map it in a weird way.
Later, you're going to have to add conditionals to figure out what you really want. What you really want to know is the stuff on the left but what you have is something on the right. That's messed up. I've dealt with data models like this before. You're using someone else's XML model for the thing and...He's laughing over there. [laughs]
You're like, "How...? What is this?" I know it says it's this thing but it could be either of the two.
Another problem. This problem is you have too many cases in your data model. Let's just say, the easy case is you have one too many. You have 13 fuel types but you only have 12 in the real world. What do you do?
Maybe the case goes unused. Do you check for it? Do you see, "Oh, you use that case. We put it in but then we regretted it. You shouldn't use it." This happens in our code over time. This kind of stuff happens.
The other thing is you could have something that you think, "Oh, maybe I know better than the domain experts. I'm going to map this one. I'm going to split it in two." You have this problem again where you have the special case with another conditional like, "Don't do this."
The other thing is sometimes you map your null. Something becomes null when it shouldn't have been null. That's a problem, too.
Then you have the opposite problem. You have one too few cases. This is where you have your 12 rocket fuel types but you just drop one. It didn't show up in your tests and you just didn't represent it, and it was fine until it's not fine.
In the other case, you try to map it to one of the existing ones. Then you have this whole problem of decoding it again. You say, "Well, I'm going to put..."
I use some software that uses both Stripe and PayPal and they have a field where they put...If the person paid with PayPal, they'll put the PayPal transaction ID. If they pay with Stripe, they put the Stripe customer ID or something. Then they have to use a regular expression to figure out which one it was.
"Is this a Stripe customer? I don't know. Let me see. Does it start with C-U-S?" That's what I'm talking about. This happens all the time.
This is two cases, Stripe and PayPal, totally different, and they're mixing them up and now they have to figure out again which one was which and you can't validate it. You can't be like, "Oh, was this really a Stripe customer?" Anyway, it bugs me but it happens all the time.
Of course, all of these create conditionals. You need conditionals in your code to stay correct, so we're just adding to the complexity. What I'm trying to say is that you can do this analysis in the data. You can draw these little box diagrams and arrows, and figure out exactly how your data is wrong.
It's much harder to do that in just straight-up code. You're looking at a conditional and you're like, "Does this have too many cases? I don't know." But if you have a data model, it's possible. Good data modeling can actually reduce the complexity of your code. I believe that is the main reason why functional programmers like to push more stuff into data.
Object-oriented people like data with methods, like behavior and data mixed together. Functional programmers like the data on the side. Keep it separate. It reduces the complexity because you can analyze it and you can figure out if it's a good model.
I'm calling that you can control the number of cases. You can't really do that with a function. It's much harder. There's too many ways to do if statements and stuff on arbitrary stuff. If it's Tuesday, do this.
Last section. Oh, I've got time. This one is the least developed section, hence it is third. This is all about complexity due to unforeseen changes. Architecture is a kind of contested term in software. What does it mean? I think that I'm choosing a practical definition. This is the stuff that functional programmers do that fits under that umbrella of...
You make choices at the beginning of your software, like what database to run, what language you're going to use, maybe what runtime, what platform, those kinds of things. Then you have to deal with those. Those are the hardest things to change. Once you've got data in there and real customer data in there, it's millions of dollars to migrate it.
We want to guard against that. We want to make it as easy as possible to fix mistakes. If we made a bad choice, we want to be able to undo it. That's what architecture is and functional programming has a few things to say about that. One is called stratified design.
Who's read "Structure and Interpretation of Computer Programs"? [sighs] Disappointed. [laughs] OK, you should read that book but I'm going to give you...They talk about this in one of the chapters. The idea is you're building layers of functionality, one semantic layer at a time.
Here, I'm going to switch to a diagram. Let's say you're writing software about cooking. At the bottom, you are going to start with the fundamental pieces, the most basic things. This is proteins, acids, and heat. We're talking this is good software. This is really getting at the heart of cooking.
Then you're going to add, on top of that, built out of the parts that you just built. You just modeled chemistry. You just modeled macro-nutrients, and thermodynamics, and all sorts of acids, and bases, and stuff like that.
Now you're going to build your cooking techniques on top of those. How do you apply heat? You use a steel pan because it conducts heat better. A dry heat, or you're going to put it on a barbecue and you get a different kind of heat.
Then out of that, you're going to build your basics of that cuisine. In New Orleans, we'll talk about trinity. OK, you start a sauce with this, or you start a dish with this, or you start with a roux. This is how you brown your meat, those kinds of things. Browning your meat is just applying heat, but it's a new thing. It's applying heat to meat so there's a lot of stuff going on there.
On top of all that, you're going to add another layer of meaning, which is, "OK, this is how you make a gumbo." But you have all the blocks, the building blocks, to make the gumbo. What's nice is you can see that these layers...this is the design part because you have to make these choices of like, "Where do these layers go? What belongs where?"
You can start to see it that you have these dependencies, like the stuff at the top depends down. Nothing is going to depend up. Chemistry's never going to depend on jambalaya, but jambalaya is going to depend on chemistry.
As you code, you start to see these. What these do is...Chemistry, when you look at it, it's this nice foundation. It's architectural. You're building a foundation of timeless routines, algorithms and things, to calculate chemistry. They're timeless because chemistry changes way slower than laws of the universe, being what they are. Change way lower than how to make a sauce.
OK, let's move on. There's one other thing that I didn't get a slide for. It's called the onion architecture. Is anyone familiar with the onion architecture? Oh, wow. OK, because I just learned about this maybe a couple of weeks ago.
Onion architecture, I'll explain it briefly. The idea is it's layers, but instead of being like bottom to top, it's inside out like an onion. The stuff on the inside is your domain model. You could think of the very middle as all your data model and the operations on that data model. Then, on top of that, you're going to build your business rules.
These are all the things that say, "We only serve red beans and rice on Monday," and that kind of thing. How to make red beans and rice is down in the core.
Then you're going to have another layer that's the application layer. This is receiving and sending messages to other systems, like you'll have a persistence, a way to persist stuff, a way to store in a database. You'll have a way to show a UI of what's going on. But all of that has to go through the data model.
You're isolating your domain, your business logic and all that stuff in a pure, pure code. This is the functional stuff. It's all functional code on the inside, calculations and data, but you're isolating that from technological choices. What GUI is being shown? What database are we talking to? That kind of thing, like what file system we're using. That's another kind of functional architecture.
Now we're going to talk about what functional programming is, just briefly. This is the picture again where you have actions. Everything's an action. I talked about this before that if you had a timeline that just ran a computation and is isolated from everything else, it doesn't really add to the histories. It's isolated.
You can think of a calculation like an action that doesn't depend on time. We do change registers in our machine. There's mutation happening. We've just created this little cocoon, this little bubble where it doesn't matter.
Then lambda calculus is Turing complete, and calculations and functions are lambda calculus. You can represent all data with lambda calculus. That's just a mathematical thing.
I don't want to explain it right now but you can represent numbers using just functions and all the operations on numbers, so data is actually calculations. It could be. It's not actually represented that way, just like calculations aren't represented as real, pure functions like mathematical abstractions.
I've come up with these little icons to represent them. I'll talk about that so maybe it helps to understand, because this is like the fundamental superpower of functional programmers. Like, if you couldn't do this, if you couldn't say, "Ah, action. Ah, data. Calculation," you're not ready yet. You're not there yet. That's why I'm focusing on this so much.
When we act in the world we use our hands, mostly. That's why I'm putting actions in there. It affects the world. It changes things. A lot of the things that we do that are calculations...because this is the way we see the world, not just our software.
A lot of the things that are calculations are hidden from our view because we're doing them in our heads. We're calculating like, "OK, given that I need milk and given that my route is going to pass by the grocery store, do I have enough time to stop by on the way home?" You're not affecting the world. You're just calculating it.
It's not like you're driving and you're going to pick up milk and then you say, "Ah, I didn't have time. Well, I wish I could do that over." No, you're not affecting the world. It's all happening in your head.
A lot of the stuff we do in software is the same way. We don't see it. It's like there's these four lines in here inside of this big routine that actually is a pure function. We can take it right out and it wouldn't be as big of an action. It wouldn't be so many actions together causing these explosion of histories.
The last one is data and I put paper. I don't know if that's a good paper icon. Did you know that was paper? I don't know. They're free icons so what am I going to do? This paper is record keeping. We've been keeping records, humans have, for a long time, thousands of years, ten thousand years, maybe a little more.
There's a lot to be said about that so when you keep records you try to make them last as long as possible. You try to put them on something durable, a clay tablet. You tie knots in things. You seal it up in a vault and you protect it from the elements. Maybe you put in paper and you dig a hole in a mountain and you put it...
This is what the Egyptians were doing. They buried the stuff. We can still read it, which is kind of cool.
The thing is when we moved to computers, we're just like, "Yeah, change it. Oh yeah, just update that row in the database. Don't worry. I'll just delete it." [laughs] We're like, "Ah!" The Egyptians are like, "You know how much work it was to keep that paper so you could still read it?" and here you are just writing all over your data.
It's ridiculous. They did it for good reasons. Same reasons why we had the Y2K problem — to save space, so use two digits instead of four. It saves half the space. Of course, now we don't have a problem of space. We have too much space. We're not going to run out, so might as well just keep it and then you can use it for a long time.
Of course, now we have other problems because of that. We have the big data and stuff.
Data is just facts about events, that's what it says in the dictionary. I just looked it up, facts about events. Things happen in the world. You somehow learn about them in your computer, like a sensor gives you a reading, or a message comes in from the Internet. Something happens and you record it.
You write it down or maybe you don't record it, maybe it's transient, but it's data. This happened. It doesn't make sense to change it. This happened. Like I got sick. You go to the doctor. They prescribe you some medicine. You go home. They write it down and you get better.
You come back to the doctor's office and you're like, "I'm better. Thank you. That really helped." They just take that paper and they tear it up. There's OK, good. Great. Awesome. That doesn't happen.
All these people outside of computers are trying so hard to make the records that don't...I'm sorry. I get emotional about this. There's laws that say they cannot do that, and yet in the computer we are just throwing it away.
Back to calculations. The real thing that we're doing with calculations usually is like making a decision, or planning. That decision is like an event. It's generating data.
If you're walking around the supermarket, you could look at different things and say, "Do I want that? Do I want that? Do I want that?" and you're like "Yes. No. No," and you just put right stuff in your basket.
Or you could, before you go, which you probably learned to do after the first couple of times like, "Why don't I look in my fridge and see what I have and what I need. Then I will plan ahead of time, and I'll write on a piece of paper what I'm going to buy. Then I don't have to look at every single thing in the supermarket and wonder, "Do I have this?"
This is something we do naturally and we need to start doing it more in our software. It's something we've just forgotten to transfer over. We write a for loop that's reading straight from the database, looping through each row and deciding, "Do I send this guy an email?" Then, "Yes, OK, send it."
How many emails are you going to send? "I don't know until I send them." Well, is that going to overload your server? "I don't know. We'll see," but why don't you read them all in, decide which ones need an email and make a list.
You made the data, this is your decision. These are the people who need email. Then you send that to the next step which goes through that and sends the emails. It's just about pulling that out. Look how much code now is not dependent on time.
It takes something, probably the result of a database query, but you could fake it out for a test if you needed to, makes a calculation, a decision, and then generates more data. So easy to test.
Last thing I want to talk about real quick is how things compose. I feel like it's important to explain why it is so distasteful to people to do functional programming, because a lot of people are like, "Oh, I could never do that but I need side effects." It's similar to ways people thought about, "I need go tos," but we need to explain why that feeling is there.
Actions are universal. Everything is an action. So to say we're not going to have actions or we're going to minimize them, it gives a feeling like, "Oh, but that's all I got. " It's all actions. I need actions, and you do need actions. You need to send that email. You need to wire the money or whatever your software does, launch the rocket, make gumbo.
Another thing that happens is you have to look at how things compose. Why is functional programming so hard? If you have two actions, they're going to make a new action if you put them together. Like you run one action, then run another action, and you bundle that up in a subroutine. It's a new action that you've made.
Same thing, if you have an action that reads in a file and then the calculation that parses the file, it reads it into a string. Now it's data. Parses it. You're doing an action and then a calculation. That's an action, because it's reading the file.
Same thing if you have action and data. You have some data hard-coded in your function that says what URL to hit. It's going to hit the URL. That's action plus data. It's making a new action.
Now, let's look, if you have two calculations, that's another calculation. That's easy. Addition and multiplication is going to make another calculation. Calculation plus data, also calculation. Then you have data and data, more data.
A couple of properties of this. It's way easier to make an action. Once you let the action in, then it just goes everywhere. Everything that's built out of that, it's an action. Once you let it in, it's going to multiply.
Same thing with calculations. If you start using calculations, you can't get data back. You can never go backwards. You'd have to modify your code. You have to do a big refactoring. But then look at data. If all you're using is data, you're safe. You're just there. I'm just in this land of data. You don't have to worry about ever accidentally making a calculation. That's nice, too.
The problem is that because it's easier to make actions, things just naturally tend to become less functional. It takes work, energy to reverse that entropy.
I'm done. My name is Eric Normand. Thank you so much.