!= Building Functional Islands - Sessions by Pusher

Building Functional Islands

Mark Jones speaking at Front-End London in April, 2016
285Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

This talk will introduce the techniques and concepts you’ll need to begin building functional islands. In this talk, Mark explain how to introduce functional programming into your code base, as well as why this might be beneficial, in terms we can all understand.


Transcript


[00:00:10] My name is Mark and I’m a JavaScript engineer at a company called, “Kahoot!” Basically, today I’m going to be talking about something called building functional islands. Unfortunately, I was told that I should have added some cat gifs or something. There are no gifs, there’s lots of code, lots of words. I’ve got a laser pointer so I clearly won. To start with, I don’t actually have anything to win but just a bit of audience participation, I’m quite keen to engage the room. If everyone could stand up. Basically, I’d like everyone who knows what functional programming is to stay standing. Cool. Sorry, just to clarify, anyone that can explain the difference between functional programming and imperative object or imperative programming stay standing. That worked. Stay standing if you would say you use functional programming in your JavaScript, like ever. It doesn’t matter how much, just a little bit. Stay standing if you would say you use functional programming for the majority of what you do. You pretty much only write functional code, you very rarely write imperative, you probably don’t write JavaScript. Cool. No one who can ask too many difficult questions later. [00:01:35] What is functional programming? For those of you who don’t know, this is going to be a really simplified explanation, much like everything in my talk. I want to try and avoid too much terminology. It’s super important but I think it can cause a lot of confusing when you’re learning. I think most functional programs would argue that you need the terminology immediately. We’re not going to do that but we’ll see how we go. Functional programming is programming using expressions instead of statements. We’re going to avoid changing any state, we’re going to avoid mutable data. It’s a more declarative way of programming and a functional programmer is going to tell the computer what they want, not how they want to get it. Before I go any further, I just want to – I don’t know why I’m using that – I just want to explain statements and expressions because I realise not everyone is going to know what that actually means. This is code using statements; it’s imperative, it’s in everyone’s JavaScript code. Realistically, it’s unavoidable. We have assignment statements, obviously you’re going to do this, we have control statements. Again, you might not loop like this anymore but you’re probably going to have control statements. Here, we’re got some mutable data, so we’re pushing into an array and we’re not actually assigning the return value to anything. This is mutable data. A functional programmer is not going to be too happy with you; whereas, expressions are a combination of variables, operators, and functions. They evaluate to produce a new value. For people who aren’t aware of what map does, it basically takes a list of data and it applies a function to each item. What we’re going to do is we’re going to see this expression turn into some different expressions. We’re going to times one by two, two by two, three by two, then we’re going to evaluate those expressions and it’s going to be two, four, six. We’re got our final value. It leaves our original array untouched. [00:003:28] Why, I guess is the key point? Why do I want to I want to go at functional programming? You don’t have to do functional programming, it’s academic. It’s potentially dry. Personally, I’ve seen a huge increase in FP, I’m going to say FP from now on because functional programming is really long, concepts in front-end libraries over the past couple of years, I guess. That’s what got me interested in it because I was reading blogs, people were using terminology, I had no idea what they were saying. Eventually, it results in beginning to learn Haskell, which I wouldn’t necessarily recommended anyone does, but it’s really enlightening. You start to see the concepts and why you would want to do them. It became quite apparent that these concepts might be really valuable for JavaScript as well, but there are not that many talks about the basics. As I say, most people like to go straight into the terminology rather than starting from scratch. That’s what we’re going to do. [00:04:21] We’re going to look at each of the blocks to start building functional code in our JavaScript. Unsurprisingly, functions are building block number one. There are several things about functions that we care about. First class functions are something we care about. These are just functions in JavaScript; all functions are first class. They’re first class citizens of the language, which basically means that they can be used anywhere any other value can. You can assign a variable to be a function, you can use a function as an argument. This shouldn’t be news. In some languages, you didn’t used to be able to do or still can’t do this. It’s important. They give us the ability to create higher order functions which, again, we see these all the time. Add event listener is a high order function. You’ll see them in event call-backs, you’ll see them in ray extras if you use map filter or reduce. Let’s ignore the add event listener examples. I don’t feel like it’s that useful. Here’s my trusty add function, we’re going to see a lot of this guy tonight. It basically takes two numbers, returns them added together. Then I have function that I would never recommend writing but it’s logify. What it does is it takes a function, so your core function, and it’s going to return a new function that is going to perform all of the logic of the original function but it’s also going to log out all the arguments you passed to the new function. It basically works like this, you would say logify add, so we’ve got a new logify add function, then we’re going to call it and you can see it logs out one and two and also logs out three, well, it returns three. It maintained all the original behaviour but we added some extra behaviour. [00:06:11] Why do we care about this? For higher order functions, are functions that create new functions. They might be very similar but they’re probably customised based on the parameters you passed to them. Or, they might be functions that change other functions, a bit like my logify example, where we’re decorating a function with new behaviour. We’re adding the ability for it to log its parameters, which is kind of like a functional form of inheritance or mixings for functions. Obviously, if you’re using classes, you could use extends, if you’re using objects, you could literally mix them together. Another type of function that FP cares about is pure functions. These functions only care about input and output. They have no side effects, so they don’t know anything about the state of the application, they also don’t change the state of the application. Something else about a function like add is that add is referentially transparent and that’s about as complex as the terminology will get, but this means that we can take an expression like this, so add one two plus add three four and we can basically substitute the expression for its value. We can substitute add one two with three and then we can substitute add three four with seven, so we’re substituting the value for the eventual value of the expression, and then get ten. Our program hasn’t changed; it’s exactly the same as it used to be, which is why they’re referentially transparent. Pure functions will be like that; whereas, this is the alternative. No one can tell me what do something else does, so no one can tell me what adding something else does. It’s impossible. It’s got side-effects unless for some crazy reason, do something else does nothing. I don’t know why you would do that. It’s got to read state elsewhere or mutate something else. We can’t do what I did a minute ago and replace the expression with the value. We don’t want code like this. Well, we need it, which I’ll get on to. [00:08:11] What are the other benefits of pure functions? Their testable. If you try and test a function like add, it cares about its input and output. It’s very easy to test. There’s not setup, you don’t need to mark objects or make sure the application is in a certain state before you run your test – it’s just going to work. The triviality unit test, which is brilliant because testing is hard and we should be doing it but it shouldn’t be a chore. Their portable so you can move a pure function around in your code base, you can move it between different apps. It doesn’t care about where it is. This is a brilliant thing. We can create easily reusable pieces of code. They are cacheable. Anyone that knows what memoisation is, I’ll explain very quickly, but it’s basically if you call a function with some values and then you call it again with the same values, it’s not going to actually run the code inside the function, it’s just going to return the value from cache. Pure functions let us do that. If they perform side-effects, then what would you return or what would you do? You can’t memoise a pure function. They’re also very easy to reason about and we hear this term a lot when we go to talks about JavaScript and React, usually. This is because they’re referentially transparent. It’s very easy to reason about what’s going on because we can just substitute things in our code. It can also be run in parallels, so if you happen to write node and you’ve got different processes running at the same time, the functions don’t care about the outside world. You can run them in any order. It really doesn’t matter. That’s a brilliant thing when you start writing quite complex, multi-threaded code. [00:09:47] The reality of pure functions is that not all functions can or will be pure. You need side-effects. If your app has no side-effects, your app is shit because it doesn’t do anything. If you want to read a file, if you want to make a HTTP request, if you want to handle some user input it’s a side-effect. You just have to accept that, move on. [00:10:10] Immutability is building block number two. As I said earlier, functional programmers, they like to avoid mutable data. JavaScript doesn’t really lend itself to this very well. How are we going to do this? Just quickly, immutable values, if we have data, we’ve got an array and we’re got an object and then we change the first item in that array to be two and then we change my age to be 27, it’s going to work. Unsurprisingly, why wouldn’t it work? Some people might say, “Who cares? You can mutate objects, that’s fine.” Well, as we know variable is bound to objects of references, so if you mutate it in one place, it’s going to mutate everywhere. You might want that. That might be behaviour you want and your app might rely on it but I’ll be perfectly honest, you don’t want to rely on that. What can we do? One solution is to use something called, Object.freeze and it was introduced in the ES5. Personally, when I first learned JavaScript and I saw this I was like, “Well, why would you freeze an object? Meaning that you can’t mutate it?” I didn’t really get it until I started learning about functional programming. It’s a function, Object.freeze, there’s a function that take some data and returns a frozen version. We can’t mutate an array item or we can’t mutate my age, everything stays the same. That would be great but if we take this complex object, complex in the sense that it has a nested object inside it, Object.freeze doesn’t do what you thought it did. It’s actually still going to change my age. It’s not what we want. My suggestion is to use something called, deepFreeze. It’s an MPM installer way, and your problem is solved. My age remains the same when I try and mutate it and it’s only 12 lines of code, unlike the potentially monolithic alternatives you might found out there. It’s simply a recursive Object.freeze. Yes. [00:12:17] Why are we doing this? We want to guarantee the values haven’t changed. It’s going to help us avoid mutation and it’s going to bring a sense of predictability to our code. It’s also hopefully going to restore some sanity. I don’t think every JavaScript developer realises the pain mutability is going to cause them regularly. You’re not going to be chasing down why objects have been mutated in different parts of your application, if it’s frozen. As I say, you might want to depend on that. It’s fine. [00:12:47] The reality of mutability is that it makes mutating objects harder. That’s just a fact. If it’s frozen, you can’t change it; therefore, you have to do something first to change it. What we would do is we would clone it and basically what we’re doing is we’re doing very deep clone of an object and this is expensive in terms of memory. Personally, I don’t really care too much, which some people might say, “You’re crazy. You should care.” If you do care, there are libraries like MORY, which implement closure, immutable data structure and there’s an immutable JS, which is a Facebook version of something similar. They don’t have the same penalty to memory because of the fact that their persistent data structures and they basically reuse different parts of the structures and it kind of avoids the problem. Also, not everything should be immutable necessarily. This is not a rule; it’s just something might help us. Personally, I find if you avoid mutation just by convention, it’s going to make you happy. If you’re not mutating all the time, you’re probably going to be a happier JavaScript developer. [00:13:52] We’ve got functions, we’ve got immutability, what’s next? Currying is what’s next and I don’t know about everyone else but when I learned JavaScript and I read about Currying, I just thought, “What is this thing? One, why is it called that? Two, what’s it for?” I just didn’t understand it. Basically, what you’ll find out is if you start learning about functional programming is it’s super important. In Haskell, all functions are curried. There are no un-curried functions. Bearing in mind, I’m just a beginner so that might not be true. Facts, when people say things in front of you, you’ve got to believe them on stage, yes. Yes, actually, this next section does make more sense in a language like Haskell because it’s the norm. In JavaScript it’s not. I’ll just explain and see how we go. Currying allows us to create functions that don’t need to be called with the same amount of arguments as there are parameters. We can create functions if we call – this ad for example – we’ve got a function, add x plus y and we’re using this thing “Curry”, this is a utility that you don’t need to worry about how it works. You’ll be able to get it from quite a few helper libraries and it basically means that this function “add”, that I can call it with only one argument, even though it takes two. What I’m doing is creating a successor function, which is basically just going to add one to what I give to it. So, if I call it with one, it’s going to give me two back, which obviously, hopefully, is blowing your mind and you believe functional programming is the right thing to do now. Please note, the function body of add, actually, this x plus y, is doesn’t run until you provide all the arguments and that can cause a bit of confusion. If you’ve put a debugger in there, for example, it’s not going to run. That caught me out a little bit when I was learning this stuff. Let’s use a slightly better example, functional programming is not just all about basic mathematics. We’ve got some developers, we’re got two people in an array and we want to get their first names. This is quite a standard way of doing this. We’re going to map over our devs and we’ve got a call-back function that gives us back the first prop. Sorry, it gives us back the first name prop of each of the developers. This could be shortened with an array function, one liner, which I think is quite a common criticism of examples like this. To me, it still has some issues even if we made it shorter. We’re mentioning our dev quite a few times for no real reason. We’re mentioning devs. We’re coupling our code to our implementation. This is code to get first names of something. It’s not code get first names of devs. We could make a function that extracts this but the call-back function, the return dev first name for example, I would say that still doesn’t really serve any purpose, except for it’s essential. Here’s the same example using curried functions, I’m going to explain a few helpers. The grey text is a lot more visible than I realise it would be. It was meant to be really hidden. Basically, prop takes the key and object. It’s going to take a key, then an object, and then it’s going to give us the value of the key of the object. I’m currying prop with firstName. this is a function that will give me the first name of any object. This is partially applied; I imagine as a concept that quite a few people are aware of. If you have curried functions, you can partially apply them. That’s probably not technically the super scientific version but it makes sense to me. Map, we’ve also partially applied it. We’ve given it a function, as I just explained, was prop first name. it’s waiting for an array of data. This is actually unusual when you’re used to using map, because map usually takes data, then a function. The arguments might seem a little bit backwards compared to the even the examples I’ve given in the talk. Curried functions always take their data last. This is because it lets us create reasonable functions. If we were currying with data, it wouldn’t be reusable but if we’re currying with functions that do things, we can reuse this. There’s a really, really good talk called, “Underscore you’re doing it wrong” by Brian Lonsdorf that explains it much, much better. It’s an interesting thing to see. In this code, we’re never saying, “Do something with devs.” Well, except for here. This “get first names”, it’s called a point three function, it doesn’t mention the data it operates on. This is a style of programming that functional programmers are super into. They’re going to strive to be point three. They’re separating the functionality from the data, which is going to aid reusability. My “get first names”, admittedly, it’s a contrived example but I can get the first names of anything, not just devs now. This is quite cool. [00:19:32] Currying. It aids composition, which is actually something for the next section and we’ve heard compositions tonight already, which is good. The point three style might increase reuse because it’s not coupled to the implementation. We’re not using unnecessary call-backs as much. I think you’d actually be quite surprised how often a curried function in a map, for example, means you don’t have to have the call-back whatsoever. You are using one but you’re creating these anonymous functions that serve very little purpose. [00:20:05] The reality of currying is if you’re used to using array extras like map and filter and reduce, as in you’re calling map on an array. Or, you’re using Low Dash or you’re using underscore. They’re not going to do the trick. You want to use something like Render or Low Dash FP because they’ll give you a curry friendly toolkit. All of the functions in Render and Low Dash FP, which is a functional version of Low Dash at curry by default. The parameters are flipped so they’re curry friendly. Get data last each time. I’m tell you, use different tools. This might be an alien concept to your team already, let alone an alien library. That needs to be considered, I would say, before you start doing this thing. We’re creating a small surf serif for bugs, which I say is a win. Also, point three shouldn’t always be aimed for, sometimes you just have to accept your code is not point three and it might be tied to something. You don’t want to make your code less readable because you’re trying to be point three, which I think is very common in Haskell. [00:21:14] This is what it’s all been building up for. Block four, it’s the final building block of what I’m going to go into in terms of functional programming. It’s made possible because of the other blocks. This is usually where mathematics starts appearing in functional programming quite heavily. I should talk about composition law, associativity, and category theory but I’m not going to talk about any of those things because it’s the last talk and I don’t feel like I can explain it that well. My explanation is going to be math free. What’s the end results we’re after when we’re composing? What we want to do is we want to take a value, we want to call a function with it, so I’m calling G with my value 1. Then I want to take the result of that and call another function with it. We’re calling F with the result of calling G with our value. I’d say this is kind of an imperative approach to a very basic function composition. We’ll look at the functional approach in a second. I’d just quickly like to introduce another helper before we start getting a little bit more confusing. We could have written the previous example like that. We get the same result. We’re calling G with our value and then we’re calling F with the result of calling G with our value. This looks okay, I guess. We could do this. Compose is another helper you can find in things like Low Dash or Render. Basically, it calls each function from the right to the left, so it’s going to go G with the value and then F with the result of that. It basically pipes the result of calling each function with a value from right to left, which is actually exactly the same as this, the way you read it from right to left. It’s not very different but let’s look at a more realistic example of what I’m trying to explain. We got a response from the server and we want to do a few things to it. We want to JSON.parse it, we want to get the film’s key value, and then we want to get the ids of each of those films. I think that’s not very easy to understand. It’s got an id. The films basically just have an id. That’s what we need to know. As I say, JSON.parse a response, we’re going to get the film, so we’re going to get the key films off of it and then we’re going to get the ids and get an array. Now, we’re using real function names, not just F and G. I’d say this code is not particularly pretty. I don’t really want to write that. It says, “Becoming hard to read, it’s becoming hard to extend. If we want to add more steps to this.” If we want to add error handling to this, it’s going to get really hard. How can composition help? This is the functional version using compose. It does the same stuff. It parse’s the JSON, it gets the film’s key off of the object using our curried prop function. Then it gets the ids off each of those films. I’ve got a curried map function here with a curried prop id. Then we’re composing the whole lot. I think this is quite important, once you’re used to composition and once you’re used to currying and once they’re clear in your mind, this is easier to read, this is easier to extend, and it’s more declarative. We’re saying our “get film ids from response” is a composition of getting the ids off of the film’s prop and then it’s off of the parse JSON. We’re using all of the things we’ve just seen. We’re looking at higher order functions, pure functions, that’s really important, pure, I’ll explain that in a sec, and curried functions. [00:25:28] What would we go to all this effort? Apps should be built from lots of smaller parts. I feel like most people would agree with me there. Composition is one of those tools that we have to do this. It’s very easy to refactor code that’s written like this because it’s hard to show on a slide, but once you start seeing more and more composition, you can start extracting composes out into small functions that you might be able to reuse and they might be point three, so they’re actually agnostics to the data they’re operating on. This is going to greatly increase the user ability of your code. [00:25:56] As with everything I have spoken about tonight, there’s a reality here. It takes a while to adjust to programming like this. Also, powerful tools in the wrong hands might be dangerous. I’m sure there’s some terrible, terrible code written like this out here. It requires pure functions to be sane. You can’t reason about code like this if you don’t know that that doesn’t cause a side-effect somewhere. We want pure functions. Actually, you can compose all sorts of things. The reality is, this is just a taste of what you could be doing. You can map and compose over things like input, output, promises, so value that aren’t there yet, error handling can be done like this as well. They’re completely outside the scope of this talk and I say I’m still learning, myself. I wouldn’t necessarily be going that far. This isn’t Haskell and I don’t think it should be. [00:27:00] It you’re still confused about what is a functional island. Not all of your codebase need to be functional. Even in Haskell, you need to make trade-offs, so even in a strict functional language you have to have side-effects. As I said, you have to do something. How big the pure island you create is, is up to you; you can make it as big or as small as you want. You may even have some islands you don’t know about in your imperative codebase already. All I’m hoping is that this is enough information that you can now see that you’re already using functional programming concepts. You hopefully will leave knowing how and why you might want to use it more. Yes, if you’re interested in learning how to do a bit more of this, I will put these slides up as always. Just here are some good free resources about learning this kind of stuff. Here are some really, really good paid ones. The bottom one is crazy but really cool. That’s everything I’ve got. Thank you.