 Thanks for coming. I'm Steffi. I'm a software engineer at Etsy. And in my master's, I did a lot of functional programming in Haskell. I wrote a type checker for a compiler for a domain-specific language for dynamic programming problems. Haskell was also my first programming language, so I might be a bit unconventional. I still find the power of functional programming amazing, and that's why I want to talk about this today. But the tool to work with will be JavaScript, of course. That's why we're here. So together with a bunch of friends, we were thinking about how to make functional programming more accessible, because it's so powerful. And for this, it's great to start with a language that's familiar, like JavaScript, so we can focus on the concepts. And since new concepts can be difficult, we will use cooking analogies to make things more understandable and learn about curry cooking along the way. I personally don't like that programming books have examples of building cars, because I ride a bike. But I think cooking is a very good example, because it's very algorithmic, and everybody has to eat. Curry cooking starts with frying spices in a pan. That's what we're doing now. So awesome. Let's get started. Let's talk about programming. Programming is awesome. I love programming. But also, it's sometimes hard. It's hard when the code is too complex. So in reality, it looks a bit more like this. Each problem brings its own complexity, right? And it's our job to build a program to solve that. So when we start, we're creating this mental workspace. It is all our useful things in it. And also, it has some kind of a history. So when we walk away from the code, there's already history when we work on it for a while. So over time, things can get messy like this. And we could also say that we add unnecessary complexity to the problem. We are adding things that are not there because of the problem, but because for historical reasons. So this is not ideal, right? So we said our workspace has all our useful things in it For a curry recipe, we need spices. So you can see spices there. Then we need a base for the sauce because curry comes actually from the word sauce. And we need a main ingredient, which could be one of those. Holy shit, that's a lot of ingredients. Let's say we pick five spices, one base for the sauce, and one main ingredient for my recipe. It's already way more combinations than I can easily oversee. I don't know about you, but no. So in functional programming and in cooking, we like to decompose our problems into smaller subproblems so that we can leverage the power of combinations of solutions instead of drowning in combinations of problems. So here you can see I have this function shuffle. I didn't write it here because it's too long for the slide. And then it just shuffles an array of stuff. And I have this function pick, which picks from the shuffled array. And I have a function print item, which just prints an item with an asterisk in front. And with this, I can already do something like pick five spices and then map this print item function over them. So my compose recipe function, it's pretty much human readable, I would say. I pick something from each ingredient group, like here, my soup base, my spices, and my main ingredient. And then I print the resulting recipe. So we just used some really great ideas to keep our workspace clean. First of all, having a good set of basic functions that we can compose and combine to achieve something more complex. And also, it should be clear whether the order of steps is important or not. If it's not important, it should be up to us. So we all model them as independent tiny functions. And second, capturing all side effects in our recipe to avoid surprises. So when we put the right ingredients into a recipe, we should always be rewarded with the correct result or the same delicious dish we speak about cooking. So if the temperature of the ingredients is not specified, a change of temperature shouldn't affect the result. So no secret side effects, no leaving the recipe fiddling with the flour and coming back, that's not allowed. No. We're just restricted to this. So if we program in this way, we can often grasp what the function does by just looking at its input and output. And that's all. OK, now let's really get started. Let's talk about abstraction. Some famous person in computer science once said, the art of programming is the art of organizing complexity, of mastering multitude, and avoiding its bastard chaos. I really like that quote. And he also said, we all know that the only mental tool by means of which a very finite piece of reasoning or a very small brain can cover a myriad of cases is called abstraction. So we have a problem, and we have some kind of solution. And when we code, we control the program flow, and we make decisions all the time in order to solve our problem. So as a consequence, many programs are combinations of these decisions. And they're creating a huge space of possible paths through the program and through the states of our workspace. And we're supposed to keep all these states in our head. And if we forget about one, we create unexpected behavior ranging from a small bug to a gaping security or like we saw in the previous talk. And our only tool for keeping this combinatorial complexity at bay and weed out this unnecessary complexity that I spoke about because we can make it obvious is abstraction. That's all we have. But the good news is we're already using it. So how can we use it? We're already using it, great. Because in any style of programming, we're using it. Functional programming just builds on abstractions that we know already. Let's go through them real quick. I know you know all this, but we are going to something later, we need it now. So the basis of programming is so simple that we often forget about it. It's just abstracting from a value. So by assigning it to a variable, we give it a name. And we can use it by calling that name later. So I have this veggies here. I don't have to write this all the time. It's just easier for me. And in the functional programming world, we don't make a big difference between a value and a function when we name them. We can also assign a function to a variable. As you know, in JavaScript, we say functions are first class citizens. But wait, we didn't talk about functions yet. So building on this, what's the next more abstract building block of a program? I would say statements and blocks of statements. And to abstract from a block and give it a name, we can create a function. We all know that, right? So when we call the function, we can pass values into it to make them available in the function statements. I'm not doing this here. You all notice. But we have to say that function abstraction is everywhere. And it's a bit undervalued, in my opinion, because it's the main means of code organization. It's just great. And it's a core of functional programming. That's why it's called like that. So we could say we build toolboxes of functions, assign them to variables, pass them to other functions, and combine them. Did I just say we pass functions to other functions? Functions that receive other functions as arguments and can return functions as results are called higher order functions. They're the next level of abstraction, abstracting from function abstractions. So I'm passing here another function to my sort function. I can do something like sort by name, sort by color. All of these are really simple. Sorry for this easy example, but you see where I'm getting it. By passing this function as an argument, I can supply a part of the behavior. So I could say I'm configuring the behavior of this higher order function. And this technique is less used in procedural programming, and it's not possible in all languages. But in JavaScript, we know it really well because of all the callbacks that we have in asynchronous JavaScript. And then on the top level of our hierarchy in our usual program, we have sets of functions. These functions are the tools in our toolbox. So we could say abstraction gives us a hierarchy to structure our programs, introduce this level of detail, and on each level, it gives us contained building blocks. Also, we didn't talk about data yet, and that's OK because we are learning to think in terms of functions first. So functions, functions, functions. With these levels of abstraction, don't I just have a tree-like composition for a program like in any imperative programming language? Yeah, that's right. How is it different? That's right. It's in fact the same. And that's why it was possible for me to move around between functional imperative world and my master's thesis. There, I took a program that was expressed in recurrences. That means recursive functions that are evaluated like in mathematics. And I could translate it into programs that fill tables. In an efficient way, and in a smart order, using loops. So to become faster, I had to use iterations and loops. And both paradigms focus on different areas. So we have the same building blocks, but it's a bit different. Let's have a look. So every time I write a loop statement, I need a variable to evaluate whether it's time to leave the loop. What happens if I have no state? I would have to use a recursive function instead. So we would focus more on functions than on statements. That's why I made this bigger. The cool thing is that a function is like a complete little package of abstraction that captures a behavior. So we could always swap it out for another function to get a different behavior. And a lot of magic is also the stuff we can do with functions to combine them and leverage their power. We use special higher order functions for this. And this is why I also made these big. So let's be honest. Isn't this slow? Some functions that we see here today might be slower. So you should watch your inner loops and look at the performance, as you always should. But considering this, I am optimizing for some other time than computation time, developer time. By keeping the code elegant, we can keep it maintainable because we can find stuff faster. And we can reuse and recombine functionality. It's more fun to cook with a well-capped food pantry, stuck with functions. OK. Looking at our toolbox of functions, are there other ways to leverage the power of functions? How about preserving the environment? We like that at Etsy. Closures can capture values at the places where the closure comes to life. So a closure is a function that captures the external bindings. I'm not talking about arguments here, which are contained in the scope where the closure was created. So we're talking about this inner function here and the namaste. And that's the value that's captured. And it's captured in the closure for a later use. This can help us to build abstractions because we can record a certain configuration or environment of variables and even functions. So why did I talk about curry all the time? Wouldn't it be great if we could start computing even before we have all our stuff ready? To do that, we have to talk about currying and partial application. I still have to eat more from the knowledge tree. So this is a curry tree. On our lucky day, we might find the leaves in the curry spice mixture. Most of the time, it's not in there. And this is Haskell Curry. He ate so much from the knowledge tree of combinatorial logic that he has his own function, the curry function, and he has a programming language named after his first and last name. In the literature and in theory and in Haskell, which is almost the same thing, currying is the transformation of a function with n arguments into n functions with one argument each. So let's have a look. Here we see a function with three arguments, ABC. It just prints them, right? The curried version consists of a function returning a function, returning a function. And each one of these has just one argument and it returns a function that expects the remaining arguments. The difference is visible in the call of the normal and the curried version. You see it already. Let's have a look. So we can supply the parameters ABC one by one. So up there I have to put them in all at once and here I can put them in one by one. And in the same way I could pre-fill the arguments into the function up to a specific depth instead of one by one. So I could do something like Haskell B and I don't know the last name yet. Can do that later. And reading more about Haskell Curry's biography is fascinating because he liked combinatorial logic and cooking, he liked both. So this is a quote from the biography, cooking also played a role in the growth of interest in combinatorial logic. Interesting. Let's also revisit higher order functions real quick. They can help us do repeated work on data structures. Map reduce and filter are some of the most prominent higher order functions. And in JavaScript they work on arrays. So we can say map applies a supplied unary function with one argument to each array element. We already used this. So if we did pick five spices, map print item up there, we had our spices array here and then what did the map actually do? How did we come to this result? So the map applies the print item function to each array element and that's how we get this output. So it's like a transformation. And then reduce combines an array of elements into a single result value via repeated application of a binary function. So the binary function gets two things in, one thing out. So it becomes smaller and smaller. That's why it's reducing. So if we wanna sum up all items in our pantry, we could do something like this. So here I did individual counts. So remember we had 10 spices, three soup bases and then the five main ingredients. And plus is the binary function. I just reroll it here because the operator works a bit differently. And we can apply it repeatedly when we sum up. So that's clearly a pattern for reduce which would look like this. So we do plus 10 and 313 and then plus and we get the 18, we get the overall sum. And then there's filter. And filter filters the elements of an array based on a predicate. So it's more like a select process. Let's say we have an array of veggies and we wanna filter what's in stock. So we can start cooking. We filter for quantity greater zero. So that's what I do here, quantity greater zero. And we see we have 25 okras so those are fine and the carrots they get kicked out because they're not in stock. So closures, curing and as we have learned partial application and these higher order functions help us to build complex and specialized functions from simple, more general functions. All right, let's build some pipelines. Function composition. A function is something that transforms input into output as we have said in this recipe part, right? And the most straightforward way to build functions from other functions is to just plug them together, build a pipeline. Would look like this, function composition. We can write ourselves a higher order function to do this for function composition. And if we do that it just looks like the definition from a math book. F applied first and then G, it looks exactly the same. And with these tools we are now ready to build us some functions. Let's play. So here we count the pieces of okra and chop them in half. Remember we had 25 pieces of okra and I'm just chopping them in half so I get double the amount of okra. So this is great and it's really readable in my opinion. So curring closures and functions as arguments can help us specialize general functions and function composition can help us combine them to build something new. And this style of programming is actually really interesting because it can bring us closer to the declarative style in which we just tell the computer what to do but we don't say how to do it. We don't say how to do it step by step. And programs in the declarative style are usually easier to read. So we don't have to keep the state of the workspace in the head of at all times. Now I have to make a confession. I'm coming from Haskell and some things are a bit different in the Haskell world. I'm just gonna tell you for context. So in Haskell we have referential transparency. A function can be replaced with its value. It means the same thing. And this is mind boggling coming from JavaScript because there's no such thing as call or return in Haskell. We don't need it. It's the same. We have to think about functions in a sense of mathematics where if f of x equals y we can replace the left hand side f of x by the right hand side y and vice versa without worrying if the function was called and if the result is ready yet. And this simplifies reasoning about the program for both the programmer and the compiler. Also Haskell is lazy. It only computes values when they're needed and it has automatic currying. So everything is curried. I don't have to do anything for it. And it's a perfectly normal thing to just partially supply functions arguments there. Happens all the time. And it's purely functional. That means we cannot have side effects that modify our environment in a normal Haskell program. Even if we wanted to, we would have to use an explicit mechanism to perform side effects. Point free style or we could say invisible arguments. Thank you scheme. That's where it's from. So in the world of Haskell where everything is automatically curried we can just leave out the last parameter of a function which would look a bit like this. I snuck some Haskell code in here but that's the only slide that has it. And what we see here is that we can omit this XS which is my list. So if I say I'm defining my sort function and it's sort by compare size I can actually leave out this argument and it still means the same thing. And this is coming from the combination of currying and differential transparency. And it comes in really handy when building pipelines of functions because we can omit the plumbing material. We don't have to plug it together. And you all know the style of programming when you use UNIX pipes because there you don't have to mention all the arguments for each function. You can just plug it together with a pipe symbol. So it's very short. It's easier to write. So if we carry all our functions and they have just one argument so we don't need to carry them we could write our JavaScript like this. I think that's very readable. So here we select the ingredients. We map slice and dice over all of them and then we cook them. So here you can see we use map and our composition without mentioning the array it operates on. And this is called point free style because we don't have to touch the arguments. And I think it's more readable because it's so concise. So what about the data? We don't really have to worry about the data as much when we are careful about avoiding side effects. It's enough to know what comes in and goes out at each piece of the pipeline which is a function. So what we don't do is modify data or change the value of a variable already created. This would be an alteration of state. When we compute something on an array we rather return a new array and set. We could say we consider objects as frozen or immutable. And a functional programming this could be seen like a machine for transforming data. A good example for this way of thinking in the JavaScript world is the way D3 for data-driven documents works. So you put the data in and something comes out. So the TLDR of this talk so far is functions can be built from other functions by composing and combining them. And then more generic functions can be pre-filled with arguments to specialize them for a particular purpose. And that's why couriering is so important. Wow, we already tricked ourselves deep into functional programming. We could say if you understand the importance of functions and what you can do with them you understand functional programming, you're done. So what can we do with them? We pass them to other functions, couriering, composition, but is there more? A question that comes with statelessness when we try to avoid side effects is the question of how to control the program flow. So usually we have a counter for iterations and we need a variable to check if it's time to leave the loop. But this variable is usually not reflected in the input or output of a function. So it's a side effect, it's bad, we want to avoid it. And in pure functional programming we would have to change our way of thinking exactly for this reason because we wouldn't have side effects. So instead of iterating, we favor recursion. And that means we use a function that calls itself for a smaller part of the problem. It sounds uncomfortable. What's so bad about the loop? In fact, nothing's bad about it. It's just a different way of thinking about the problem. Iteration is usually bottom up whereas recursion solves the top down. Could be the same problem. And I'm very grateful that I had to alternate and map between these two words for the recurrences to tables compiler that I talked about in my masters. It's a really good brain exercise and this restriction to just functions made people construct really cool stuff like the Y combinator showing that recursion is possible with just functions and parameters and you don't need any declarations at all. I think that's one of the coolest things in computer science, it's fascinating. And if we restrict ourselves to the purely functional style, everything's expressed in the input and output of functions. Even the program flow, it's mostly a recursion over the input or output data that the function has. So when we have to deal with the internet, asynchronous code can dramatically reduce the latency in our system. It can be hard to write though. There's no way in vanilla JavaScript to say wait for the return of a function. We all know that and we supply a callback function to tell the function where to continue after it's done. And if we have a cascade of callback functions and need to do error handling, things can get hairy. That's the site callbackhell.com. I don't know if you saw this. I got this from Stack Overflow, but you get the idea. You've all seen this code which looks like a pyramid on the side. So a functional programming technique for explicit error handling would be using monads. Imagine we would be in a purely functional world where functions behave like mathematical functions. So the order in which we write our functions doesn't matter. In JavaScript, we can execute expressions in sequence. We just write one after the other and put a semicolon in between. How about making our own semicolon? Oh, that's really weird. To do that, let's observe what it does first. What does the semicolon do in this function? Function foo, it does some console log bar and then return the length. You can see I'm running it here. So the given string is printed and then the length of the string is returned. Let's write a function that behaves like the semicolon here. We wrap the expressions into anonymous functions so we can call them like we want to. We will see in a later step when they come into our semicolon function as arguments. That's what we are aiming for. And we also need an extra return so that the last value gets out of this wrapping functions that we added here. And to abstract from the two statements, we now pass the first and second expression as arguments into our semicolon function. So here you can see I have this fun A, fun B and these are my two expressions for my semicolon. The one before and the one after. And now it's also clear why we had to wrap them into these anonymous functions because of the different return behavior. And the expressions depend on each other but the first result gets thrown away. It's not appearing anywhere. We can now chain the first and second expression in the same way a semicolon can. Works, same output. In fact, we can chain as many expressions as we like. So I chain here, this works, like a semicolon. And then bang, which is the function up there, it just adds an exclamation mark. And then I get my result of 16. This sounds really boring because we already have a semicolon in JavaScript. Why did I even do that? But if you think about this for a while, we just constructed a chaining function from purely functional parts. So this is a big deal. We went from a word in which the order of functions or statements doesn't make a difference to a word in which we can change statements into sequence to establish their order. The semicolon function works like a function known as monadic bind. Monadic bind or M bind resembles function composition but throws away intermediate results and statements can be executed just for their side effects. That means with M bind we can produce side effects even if we are restricted to the purely functional programming word. That's great. What a trick. I think it's so cool. So we said monads can chain things into sequences. What kind of things? Let's imagine a container, a cooking pot. We start with pot one. So I have here a pot and it has some contents. And when we have the result from pot one, we can execute pot two. So I have pot two here. And we could go on like this. And this is a different view on the semicolon from above. Steps can be chained with M bind and after we work through the entire sequence of steps we get a result. Let's express this workflow in steps to reveal the pattern. So if you would write down the values of the computation it would look like this. So here I'm stripping away all the pots and we just talk about values. The difference between both versions is that the first one puts everything into a pot and the second one works on the naked values we could say. Let's imagine we have a really good reason to put everything in a pot. For example we could hide some additional logic in there. The pot could keep track of the sum of all the numbers that are stored in it or it could keep a list of ingredients sorted in the pot. And the question is, can we deduce a generic monad pattern from this? We want a function that applies a function to the contents inside a pot and gives us a new one containing the result of that function. We can define this function which is M bind for the pot monad on the pot-proded height. And we see a very clean pattern now if we write it like this. We also see that we need an extra step to get the contents out of that pot in the end. So here I get the contents out. So step one of the computation is A equals new pot of one. As an object-oriented programmer I say, ah, I noticed it's a constructor. We're constructing a new thing of type pot. But in the monad jargon we call this function unit or return. Return is already taken in JavaScript so we should be a bit careful. And it takes a value and transforms it into a monadic value. The next steps all have a similar pattern. We know already it uses the function M bind. So that's the second step. And M bind takes a monadic value and a function which works on the contents. We could say it works on the plain monadic type and it returns a new monadic value containing the result. So we always stay in the pot, we could say. And that's all. So why are there so many people talking about monads? Why don't we just say pattern that wraps and chains computations? First of all, it's not a good name and also it was described first by mathematicians from the field of category theory. And for the practical usage of the monad pattern is completely unimportant, you can forget about it. But what's really cool about the monad pattern in JavaScript is that we can hide stuff in it. So we get an extra layer of abstraction to hide the bookkeeping and arrow handling and we can let the beauty of the algorithm shine and bring it out. Now that we've conquered the monads, do we get dessert? I'd rather have you take something home because it's late. I hope you enjoyed some of these techniques and they were digestible a bit. And if you ever find yourself in a mess, maybe some of these techniques will help you out. And if you never get yourself into one, at least it's fun to reason about it, I enjoyed. And don't forget to take home some of the purely functional goodness from this. So first of all, abstraction, it's at the heart of everything. And then very localized understanding of code, small, tiny functions that you can reuse everywhere. And then explicit side effects, explicit arrow handling, make it visible what's happening. And then reusability resulting from this side. And as some of you might have seen on the web, all of this and more can be found in our book, which is called Das Currybuch. I'm not sure how many German speakers are in this audience. The book is in German for now. I'm terribly sorry. But the code examples are in English, so you can ease into it with the known language again, maybe. And as we have seen, it's totally possible to explore deeper functional concepts in JavaScript. Even though I still have Haskell and I snuck some Haskell in here, I'm amazed about how much elegance this programming style reveals from the core of JavaScript. I think it's just awesome. And refactoring in this functional style is really fun. Thank you for listening.