Using Functional Programming in JavaScript

I've been looking more into functional programming lately and how some of the concepts can be applied to Javascript. I'm definitely not talking about converting everything to Haskell or writing Javascript completely differently, but there are some ideas that I think can make larger applications much easier to work with.

A lot of these concepts are already pretty common in Javascript especially if you've used libraries like lodash, underscore, Immutable.js or even just methods like map, filter and reduce.

No Side Effects

One of the main concepts in functional programming is avoiding side effects. Basically given the same input, a function should return the same output without changing something outside of the function.

Instead of something like:

let total = 0;

function add(value) {  
  total += value;
}

we can do:

function add(total, value) {  
  return total + value;
}

The second function is much easier to follow because everything it needs is passed into it. We don't have to search somewhere else in the application to figure out who changed total, when it changed or what the current value is.

This sounds pretty basic but as an application grows and more things start changing the same state, figuring out what changed something can get difficult very quickly.

Less State

State is obviously unavoidable in an application. We need to store users, application settings, API responses and basically everything else the application needs to remember. The problem isn't really state itself but having too many places that can modify the same state.

For example instead of modifying an object directly:

user.name = "Taku";  

we can create a new object:

const updatedUser = {  
  ...user,
  name: "Taku"
};

Now we still have the original user object and we know exactly where the new object came from. This type of pattern becomes especially useful when working with Redux where we don't want reducers modifying the existing state.

Concurrency

Another interesting benefit is concurrency. Javascript in the browser is mostly single threaded but we already deal with a lot of asynchronous operations.

Promise.all([  
  getUsers(),
  getProducts(),
  getOrders()
]);

If these functions all depend on or modify some shared state, it becomes much easier to get unexpected results. On the other hand a function like:

function calculateTotal(items) {  
  return items.reduce((total, item) => {
    return total + item.price;
  }, 0);
}

doesn't really care what else the application is doing. We can call it whenever we want with the same data and should always get the same result.

I think this becomes more important as Javascript applications start doing more things asynchronously and potentially in parallel.

Testing

Pure functions also make testing a lot easier.

function multiply(a, b) {  
  return a * b;
}

There isn't much setup needed to test this.

multiply(2, 3) === 6;  

We don't need a database, browser, global variables or some complicated application state before running the test. Obviously an entire application can't be written this way since we still need I/O, APIs and databases, but separating as much of the business logic as possible into these types of functions makes testing much easier.

Reusing Code Between Frontend and Backend

This also works pretty well with isomorphic Javascript. If some of our Javascript is going to run on both the frontend and backend, it helps if the actual business logic isn't tightly coupled to either environment.

function calculateDiscount(price, percentage) {  
  return price - price * percentage;
}

There is nothing browser or Node specific about the above function. It can run on the frontend, backend or inside a test.

I think there is a lot of value in keeping business logic like this separate from the code responsible for actually getting or saving the data.

Composition

Another useful concept is building larger functionality by combining smaller functions.

function double(value) {  
  return value * 2;
}

function addTen(value) {  
  return value + 10;
}

function calculate(value) {  
  return addTen(double(value));
}

Each function is really simple by itself and we can test each one separately. More complicated functionality can then be built by combining these smaller functions instead of having one huge function managing a bunch of variables and state.

Javascript Is Already Pretty Functional

The nice thing is Javascript already gives us a lot of tools for doing this.

const activeUsers = users  
  .filter(user => user.active)
  .map(user => user.name);

Instead of manually creating an array, looping through users, checking each user and pushing the name into another array, we describe the transformations we want to make to the data.

Obviously I don't think Javascript applications need to be 100% functional. Applications have state, perform I/O, call APIs and save things into databases so side effects are going to happen.

But minimizing shared state, using pure functions where it makes sense and separating business logic from I/O seems like a good way to make larger Javascript applications easier to maintain.

Comments powered by Disqus