Subtitles section Play video Print subtitles Thank you for that wonderful introduction, that once expected. Yes, hello, I'm James, I'm going to talk to you today about practical functional programming, to make sure I get through this in plenty of time, and the clock's not ticking down, so I don't know how much time I've got, that's very confusing. What do I mean by functional programming? Well, initially I didn't even know, itches looking for ideas to talk about on Twitter a while ago, and Jan gave this idea, practical function fallal programming, and I brushed it off with a little juke. Got me thinking. It has first class functions, and we have venttion and call backs and we have all the array operations like map, filter, reduce and stuff like that. But, there's a lot more to functional programming than we often acknowledge. And what I would like to do is sort of demystify that a little bit. Because one criticism of functional programming, it's not very practical and you need a PhD to understand it. What I'd like to do is sort of talk about the ways in which we are already using functional ideas in mainstream JavaScript and how we can pay better attention to those and get better value out of them. Also promise the last occurrence of the word Monad in this talk, there's not going to be a lot of type theory stuff going on. What do we mean by functions? Functional programming is about programming without side effects, if you're in the Parallel JavaScript talk earlier you would have heard that side effects are one of the biggest barriers to parallelizing a program because you can't reason about changes to a global state, makes it hard to parial rise things. What does it look like to program functionally. You want to calculate the length of a list, length takes a list and returns an Int that's what that little type signature means. So, what you could do is set the counter to 0 and increment the counter for every iment and return the last value of the counter. How does that run out in practice, want to get the value of the list, set the count to zero, first element, increment the index, there's a second element, we increment, there's a third, we increment, there's no fourth element we break the loop and induction is 3. The values of the index comments ‑‑ those aren't statements that appear anywhere in the program that state you have to keep in your head while you figure out what the program dugs, that's what makes imperative programs hard to reason about. A functional version would look like this, cough free script has three dot syntax that breaks a list into the first elements and the rest of the elements. If the first element is undefined the list is empty, right, so it has length 0 otherwise the length one plus the length of the rest of the list, we're doing a recuresive break down. What does that look like in practice, so, there is a first element, so we'll pick a second bunch. We're just going to replace the definition, we're using it as a rewrite rule, no implicit state to keep track of. We do that a couple more times then we get length of the empty list, which we pick the first branch of the conditional, which is 0 and result falls out. So here rather than writing comments with sate in them, I've written "is" before each expression, that's because they're identical. Doesn't mean they have the same value at a particular point in time, they always have the same value you can replace any of these expression with any of the other and the program will do the same thing. Substituting bitting of source code for each other makes it easy to think about, you don't have to track state somewhere else in your mind or on paper. Let's take another function, map, the signature means map takes a function from A to B and a list of As and returns a list of Bs, the imperative version of that would be to take an empty list and push ever VEX for every element in the input and return the list. The functional version would be to say in is no first element, the effect is undefined then return an empty list otherwise afully F to the first element and then combine that with map over the rest of the list. Same structure as we're doing the went, we're using list instead of numbers, say we want to square the first three numbers, we go, there is a first element, so we'll take a second branch, so square of one is one, we pull that out of the front and map over what's left, map over what's left, square two and then, we have anmentty list, and so, the ‑‑ an empty list and then the result falls out. These are our functional solutions to the problem, theye they work by not my stating the state, they work by giving you an expression you're trying to calculatend that you can replace and you do that recursively. You can program by substitution by using these things. The imperative solutions work by making some state and then like changing that state until some condition becomes true and then handing that state off to you at the end. But even though those have internal state and they're not internally functional, they do exactly the same thing as these versions, because, ‑‑ they don't Mutate ‑‑ you can treat them as if they're the same functions. That's useful, it means their state is completely ensuelated the sidefects continue leek out into the rest of the program. We use that in promises. Promises have internal state, they are pending and they can become fulfilled or rejected with a value or an error. But, every time you call then it will yield the same value to you, you never get to see what's actually going on inside of the promise, Jo us call then and value pops out and it's always the same value you can treat a promise as an immutable value, which is useful. A couple weeks ago too much ash ford had this really good essay, vents are bad primitive for data flow, they require distribution of mutable state around your code and it's not idiomatic or plea Saint to flow through data through events. We can use types to answer that question, if we consider the FS.read file, that takes a path name, an encoding, a call back and it returns nothing, and the call back is itself a function that takes an error, value and alts returns nothing. Now function that returns nothing must have side effects because if a function has no return value and no sidefects what why you calling it? It's not doing anything. The thing that you think of not having side effects is reading a file we work with these things using side effects functions, completely. When we're dealing with all these asynchronous things all the time we have to make sure all the side effects happen in the right order so the program doesn't get into a bizarre state, and trying to did that on concurrent programs is very, very difficult, which you doubt know. It gets even harder when you try odo a lot of things at same time, if you want to read a file and request a URL and get something out of a data business at the same time, you use Async.parallel, and do operation, when they complete you get a cull back and you get value for those things, what would I do if I wanted to get the value of the file before the other things are completed and do something with it. Because this, if any of the things fail, I won't get any of the values, I only work on the file even if the HDP request fails, I could pull the file operation out top. I have an FS.read file ‑‑ but now I've made the program slower because the second thing are blocked on the first thing completing, so I de‑parol liesed it. That kills the performance, it's convenient, but, you've traded convenience for performance. What you actually have do is keep the parallel construct to make sure the IO happens at the same time but plug your processing into the bit where the file is requested, the more you do these things the data processing, what you're forced do by the way that we schedule things in asynchronous programs your programs get very messy, edge tangled you get into what we roll call back hell, it's not a sin tackic thing about your code creeping across the page, it's the inability to reason able when things are happening in your program and make sure they happen as efficiently as possible, to do this you is to construct your program in a very specific way with call backs in all the right places. So, last year I read this article called call backs are imperative, promises are functional, which had this quote "the future of promises is that they remain immune to change circumstances." I've already mentioned that ‑‑ I got some laughs ‑‑ I already mention how promises look like immutable values because then always gives you the same result out once the tasks are completed, but this is also true in a second way that I department realize at the same, the promises that deal in changing promises much more easily. Before when I was using the asing module I had to change my program quite radically, I could make a big structural change to it to make quite a small requirement change. But say those functions return promises my FS.read and database dot get always return promises of strings, I can call all those functions and put the results in an array and all the Io will just happen in parallel, but now I have an array of promises of strings. And if I want just the first ‑‑ if I just want the file out of that, then I can get the first promise out of the list and do something with the result. If I want to wait for all of the things to finish, then I can call promise.all with the documents and I get a promise that will give me all of the results when they're ready, if I don't want to deal with the file on it's own, I just delete the documents.zero line, I can just add lines for those, I don't have to make bige structural changes to my program. You keep the ability to keep your IO happening in parallel, but you keep the convenience of being able to work with it easily, that's really important for dealing with programs that change a lot over time. And the reason this works is that I can ask for the value out of the file promise and promise to all can also answer the value out of that file promise, and it'll work both times because you can keep asking for the value over and over again, and you won't repeat any work, what it means to be immutable, it's rejuiceble, right. So let's talk ‑‑ it's reusable. We talk about I in very imperative terms, we say then takes a function and that function will be invoked when the task completes with the value of the task, if it's completed it will be invoked with the value of the task it returns a promise and that promise will be invoked with the return of the call back, we talk about then, do this, then do this, and then do that. But if you think about the types of things that are involved, what then really does it takes a promise of A and function there A to B and returns a promise of B, if we have a promise of a string and we call then with a function that counts the word in a string, string.split on spaces.length what we get is a promise of an I Int, we might not have the Int, but that's the type of the value we have, we continue to do more processing on it, transform it into another thing, transform it into another thing, and use the promise itself as a value, not the thing that's inside of it. This is exactly the same thing a ray.map works it takes list of something and a function from A to B around returns a list of B, right, so if you have a list of strings and you map a word counting function over the list, you now have a list of word counts just as if you called then with a word counting function you turn a promise of a string into a promise of a word count, a promise is just a container, it's a list, they're the same thing, the operations are the same thing, a container of up type, container of another type with a mapping function between them, and because they're just containertion you can compos containers, they're just another type of data structure, if you have a list of promise of strings you turn that into a list of pr