One of the programming paradigms that has lately gained ground in the JS community is functional programming. If you’ve browsed medium in the past year you have undoubtedly seen posts on this topic.
Boom or bust?
What is functional programming? Where does it come from and who uses it? For some developers it sounds like an esoteric term or something distant that they find no reason to invest the time in. While the world is fully embracing object oriented programming why should you put in time to learn a completely different way of thinking about building programs?
While the world is still object oriented, learning something new will always be of benefit to your skillset. As developers we are constantly learning, but in order to strive forward you sometimes need to unlearn some concepts.
The aim of this series of articles is to shed some light on how you can put into practice the different concepts of functional programming and how this can be of benefit to you. While many tutorials stick to the generic examples of arithmetic operations that you will never put to use, here I will try to show you how you can integrate your newly found functional skills in the programs you are writing at this very moment.
Functional programming & Pure functions
Functional programming revolves around the idea that a program is made of a set of functions and they follow certain rules. There are no classes, there is no inheritance and the patterns that you will encounter are a lot different here.
The main concept of FP is the idea of pure functions. Those are functions that take some input, operate on it and return an output without modifying any variables our state outside of the scope of the function. Every function that reaches out for the DOM or uses variables that are not in it’s scope is therefore considered impure and does not step up to the standards of functional programming.
But how can a program be made entirely of pure functions? No, of course not. If you have a program made of pure functions it probably won’t be doing anything exciting. You need to be able to work with the DOM, you need to be able to send requests to services or log messages in the console. The goal of FP is to compose the majority of your program from small pieces of logic that can be combined together and reused. Side effects are inevitable but by limiting them to certain places in your application they will be easier to manage and track.
The main benefit of writing small pure functions is probably their predictability. Due to the fact that your function does not reach for external variables and does not modify anything outside of it scope you can easily tell the output of it depending on the input. That is actually one of the other rules that defines a pure function — given the same input it should always return the same output.
If you have never used functional programming before this may sound a bit counterintuitive at first. You can take this as a really strict way to enforce a separation of concerns in your code base. Every piece of logic, every function should be doing exactly one thing and it should not be interfering with how other parts of the code work.
This is an example of a pure function that I’ve been using as a filter in one of the applications I’m writing. A simple way to validate whether your function is indeed pure is by running it multiple times. If it is not using global variables or manipulating state outside of it’s scope it should always return the same input no matter how many times you call it.
This is an impure function and you should avoid writing those as much as possible. Even though you get the result you’re asking for the first time you run it any consequent call will result in a
NaN. Not to mention that something like this is completely unusable in any other scenario or in other parts of your program. The bigger goal of functional programming, as we will see in the next articles, is to be able to compose your program out of many small reusable building blocks (functions).
To further illustrate how you’ve been dealing with patterns like this all along we’ll take a look at the community’s favourite library — React. In react you can create stateless functional components — components that don’t do anything fancy and are used to display a piece of the UI without having access to lifecycle methods. These components are implemented using the concept of pure functions — they simply take an input and return JSX.
Here’s a real-life example of a component I’ve used:
No matter how many times you call them they will produce the same result and return the same JSX. You can disregard the actual structure that is returned but notice how it depends entirely on what input is passed to it, including the
onVideoSelect function. This means that this can be reused for multiple videos and if I want to handle a click on the item in a different manner I just need to pass another function.
While we are on the React topic let’s dive a little deeper and take a look at Redux. When you want to make changes to your React app’s state you need to do this using functions called reducers. Despite the name, reducers are just plain functions that take the previous state and the changes you want to make and return the new state. This does sound quite pure, doesn’t it?
Reducers are used to describe every single change in your application state and they do this in a functional manner, without accessing or modifying global variables. They work only on what you pass them as input, therefore you know that by passing certain values as input to the reducer, your application will look in a certain way. This is extremely predictable and easy to debug and test as we will see later in the article.
Let’s examine another rule which is essential to write proper pure functions — they must not modify the caller of the function. This means that you should never modify any properties on the
this object. The enigmatic
Every time you depend on
this you need to track and pay attention to how and where your function is being called. In functional programming, modifying the caller of the function is unacceptable and is considered a side effect.
The function that we’re looking at is
concat. As you can see it does not modify the function it is being called on. If you’re new to the language you might expect the hello variable to have a value of “Hello World”. However, because the concat function adheres to FP standards, the variable on which it is executed remains untouched. In fact if you want to access the returned value from concat, you need to assign it to another variable or directly pass it to something.
Much like in the previous example, the
numbers array will stay untouched. The map function will return an entirely new array. However, there is something more interesting about those functions — they are examples of the so called Higher Order Functions.
Higher Order Functions are functions which can take a function as input or return a function as output. As you can see in the example, we’re not passing a value, we’re passing a function to be executed on every element of the array.
This is something else to have in mind — functions are first class! They can be passed around as input or returned as a result of a function. This combined with the fact that they will return an entirely new array instead of modifying the callee allows us to chain them for better efficiency:
What we covered in this part hints at other parts of functional programming — immutability and function composition. In order to not make this article overcomplicated I will intentionally skip them for now.
Let’s look at pure functions from the quality assurance side of things. We all know that writing tests is of critical importance and is extremely beneficial but we don’t always do it. By using the functional approach you avoid any cheeky side effects and your function is not accessing or modifying anything from the global scope. This removes the burden of complicated injections and mocking — everything your function needs is passed as an input and you will only need to validate if the output is correct.
Testing your functions just became a whole lot easier. In fact you can look at your test cases more like a sanity check. When the building blocks of your app are self-contained and don’t do anything extraordinary you just need to make sure that everything is how it’s supposed to be. Testing becomes a process of examination. You have a listed of questions (your input parameters) and a list of answers (your output parameters) — now you only need to compare the function’s answers compared to the actual ones. Anyone in the QA department will be extremely helpful if you wrote code like that.
Every app build using a Redux like pattern is much easier to test than those that are not. Like I mentioned earlier in the article, every change in your application state is described as a pure function which has a predictable output based on it’s input. This is the simplest thing to test — we just call the function with some value.
In the example bellow I’m testing a reducer’s behaviour by passing an action he should not be responding to and another that he should. In both cases, multiple calls with the same parameters would provide for the same output.
Functional programming can be quite the overwhelming and confusing topic but in order to start feeling comfortable we need a solid understanding of it’s building blocks — the functions.
In this article we covered the applications and benefits of using pure functions in your codebase.
The next article is about immutability and you can read it here.
If you’ve reached this far, sincerely thank you for the read. Any feedback would be much appreciated and if you liked what you’ve read please hold the clap button!