Getting going with functional programming in JavaScript

Functional programming has made a strong resurgence in recent years and for good reason too. Functional software breaks code down into composable deterministic functions, resulting in code that is often simple, elegant, and expressive. It can lead to code that is easier to understand and maintain, and straightforward to test. It's now cropping up more and more in both job adverts and interviews too.

It shouldn't therefore come as a surprise that many people are now looking to gain exposure to functional programming, however the often mathematical notation can be intimidating to many.

In this article, I will throw some light on what makes code functional, and explain how you can get started reaping the benefits today.

Getting started

The good news is, if you're using libraries like React and Redux, chances are you are already making some use of some functional patterns without even realising. Redux reducer functions are free of side effects and therefore pure. But more on that later.

As stated previously, functional programming breaks code down into composable deterministic functions. Let's take a look at what that actually means:

Composable:

A highly composable system provides components that can be selected and assembled in various combinations to satisfy specific user requirements.

For functions to be composable, they must take one input and return one output. In this way, you're able to compose two or more such functions into a new function, by using the output of one as the input to the next.

Deterministic:

A deterministic system is a system in which no randomness is involved in the development of future states of the system. A deterministic model will thus always produce the same output from a given starting condition or initial state.

For functions to be deterministic, they must not depend on any outside system state. Any attempt to interact with such state is known as a side effect. By only interacting with the function inputs and returning a single output, the function becomes deterministic. We refer to these kinds of functions as pure.

Pure functions are not only straightforward to understand but they're also easy to test, as no external system state needs to be set up beforehand. In contrast, with complex class based applications this setup can soon become a real headache.

What does this look like?

So what does this actually look like? Let's break this down below, with two functions, double and square.

const double = number => number * 2;  
const square = number => number ^ 2;

console.log(double(3)); // => 6  
console.log(square(3)); // => 9  

Notice how both functions take only one input and return only one output, making them both composable. Notice also how neither function relies on any external state or makes changes to external state, making them both deterministic, aka pure, and free of side effects.

Given they are composable, we can compose these two functions to form a new function. See below for an example of this:

const doubleThenSquare = number => square(double(number));

console.log(doubleThenSquare(3)); // => 36  

Many utility libraries provide a compose method, allowing us to rewrite the previous example in a nicer syntax:

const doubleThenSquare = number => compose(square, double)(number);

console.log(doubleThenSquare(3)); // => 36  

As long as the compose function is curried (as in the example above), we can rewrite this using point free syntax like so:

const doubleThenSquare = compose(square, double);

console.log(doubleThenSquare(3)); // => 36  

Pretty neat huh?! The next thing to realise is that any function can be made composable using a technique called currying. With currying, we transform a function with multiple arguments into a sequence of functions, each with a single argument. This is made possible by the inclusion of first class functions and closures within JavaScript. When a function is curried, the inner functions are closures, having access to their own scope and the scope of their outer functions, and in this way the transformed function essentially has the same behaviour.

General functional programming principles

Now that we've covered the cornerstones of functional programming, let's go over a few more basic principles, that will help to ensure your code is simple to reason about, painless to extend, and a joy to test. Ok, maybe it still won't be a joy to test, but it should be easier!

Immutability

Immutable code is free from mutation, i.e. state is prevented from being changed. This makes code easier to understand, by removing the need to track back through your code to follow the various mutations of state, often holding several variables in your head at once. In doing so, immutable code reduces the cognitive load.

There are libraries available in JavaScript to help enforce this, but often just favouring const over let and var and avoiding changing function inputs will go a long way towards giving you the benefits of this approach.

Recursion

When following functional programming principles, recursion becomes essential. Recursion is when a function calls itself, and it provides an alternative to the non functional approach of using for loops with counters along with mutating state.

For example:

const greatestCommonDivisor = (a, b) => {  
  while (a !== b) {
    if (a > b) {
      a -= b;
    } else if (b > a) {
      b -= a;
    }
  }
};

Becomes:

const greatestCommonDivisor = (a, b) => {  
  if (a > b) {
    return greatestCommonDivisor(a - b, b);
  } else if (b > a) {
    return greatestCommonDivisor(a, b - a);
  } else {
    // a === b
    return a; 
  }
};

Functional programming libraries

As mentioned a couple of times above, there are a variety of functional programming libraries in JavaScript, providing you with things like immutable data structures, and higher order functions for performing operations such as map, reduce, omit, compose, and many more.

A personal favourite of mine and one that is popular within the community is Ramda. Check out the Ramda API documentation for example usage.

What next

After having let all this sink in, I hope you now have a better idea of what functional programming is, and feel confident enough to get started writing functional code today!

Resources

A few great resources on functional programming are: