In last week’s post, I mentioned that a higher-order function is one way to pass extra arguments to a function in JavaScript. This is closely related to a technique called currying, named after Haskell Curry, the noted mathematician and logician. Currying is the process of converting a function with multiple parameters into a series of functions, each of which has a single parameter.

Currying

Perhaps the most common example you’ll find online is a function that adds two numbers together. Before currying, the function has two parameters. It returns the sum of its parameters’ values:

const add = (a, b) => a + b;
add(1, 1); // 2

After currying, the function has a single parameter. It returns another function with a single parameter. Because the inner function is in the lexical scope of the outer function, it has access to the value of the outer function’s parameter (creating a closure). The return value of the inner function is the sum of the parameters’ values:

const add = a => b => a + b;
add(1)(1); // 2

Arrow function expressions are often used in functional programming because they closely resemble mathematical functions. However, you can certainly create curried functions with traditional function declarations and function expressions:

function add(a) {
return function (b) {
return a + b;
};
}

Partial application

If you’re not used to functional programming, this might seem silly. Why would you convert a function that returns a sum into a function that returns another function that returns a sum? The answer is partial application. This is when you call a curried function with fewer arguments than its total number of parameters. The benefit of this is that instead of calling the functions all at once (add(1)(2)), you can call them in steps to create more specific functions.

For example, you could partially apply the add() function to create increment() and decrement() functions:

const add = a => b => a + b;

const increment = add(1);
increment(5); // 6

const decrement = add(-1);
decrement(5); // 4

In fact, you could create a function that adds any number you want:

const add = a => b => a + b;

const add10 = add(10);
add10(20); // 30

const subtract15 = add(-15);
subtract15(65); // 50

This is the true benefit of currying. Once a function has been curried, you can partially apply it to create more specific functions. This makes a curried function very flexible and composable.

You can still use the function to add two numbers together:

const add = a => b => a + b;
add(6)(7); // 13

But you also have the option to partially apply it, which makes it more composable.

If you want to use both syntaxes, there are utility functions that enable this. For example, the Lodash library provides a .curry() method. The currying tutorial on JavaScript.info explains how it works.

const add = _.curry((a, b) => a + b);
add(6)(7); // 13
add(6, 7); // 13

A practical example

Adding numbers together is a great way to explain the theory of currying and partial application. But how about an example from “the real world,” which is often more messy? My favourite example I’ve found online is that of isolating expensive processes. I’ll share the code in verbatim and try to explain it in my own words, but I encourage you to read the original post: Why the fudge should I use currying?

Before currying

The idea of this example is that we have a database of global parcels, represented by the allGlobalParcels array. This is a very large array with 2,701,201,203 elements. Obviously, filtering the array will be a very expensive process, due to the sheer number of elements. Before currying the sortParcelsByCountry() function, it filters the enormous array every time we call it. To quote Rich Harris: as engineers, we should be offended by all that inefficiency!

// Given a database of global parcels like this...
const allGlobalParcels = [
{
created: 576424800000,
location: "aus",
properties: {/* ... */},
},
{
created: 1558163267311,
location: "us",
properties: {/* ... */},
},
// ...2701201201 more items
];

const sortParcelsByCountry = (parcels, country, order) => {
// 1. Filter our list to only include parcels from `country`
const countryParcels = parcels.filter(parcel => parcel.location === country);

// 2. Sort the list of parcels by date created
const sortedResult = [...countryParcels].sort((a, b) => {
if (order === "ascending") return a.created - b.created;

// By default return packages by descending order
return b.created - a.created;
});

return sortedResult;
};

const ausParcelsAsc = sortParcelsByCountry(
allGlobalParcels,
"aus",
"ascending"
);

const ausParcelsDsc = sortParcelsByCountry(
allGlobalParcels,
"aus",
"descending"
);

After currying

To fix this inefficiency, we can curry the sortParcelsByCountry() function. The outermost function will have a single parameter: parcels. It will return another function with a single parameter: country. The inner function will sort the parcels by country, then return another function with a single parameter: order. The innermost function will sort the filtered array by order and return it.

By partially applying the sortParcelsByCountry() function, we can create another function called sortAusParcelsBy(). This function is specifically for sorting Australian parcels. The expensive filtering process only happens at this stage, when we partially apply the function. The result is that we can sort the list of Australian parcels in different ways, without having to keep filtering the gargantuan array.

// Given a database of global parcels like this...
const allGlobalParcels = [
{
created: 576424800000,
location: "aus",
properties: {/* ... */},
},
{
created: 1558163267311,
location: "us",
properties: {/* ... */},
},
// ...2701201201 more items
];

const sortParcelsByCountry = parcels => country => {
// 1. Filter our list to only include parcels from `country`
const countryParcels = parcels.filter(parcel => parcel.location === country);

// We now return a function that sorts the parcels by date created
return order => {
// 2. Sort the list of packages by date
const sortedResult = [...countryParcels].sort((a, b) => {
if (order === "ascending") return a.created - b.created;

// By default return packages by descending order
return b.created - a.created;
});

return sortedResult;
};
};

// We create a new function with the filtered list of parcels by country in its closure scope
const sortAusParcelsBy = sortParcelsByCountry(allGlobalParcels)("aus");

const ausParcelsAsc = sortAusParcelsBy("ascending");
const ausParcelsDsc = sortAusParcelsBy("descending");

Summary

Currying is the process of converting a function with multiple parameters into a series of functions, each of which has a single parameter. The benefit of a curried function is that you can partially apply it. This is when you call a curried function with fewer arguments than its total number of parameters. The benefit of partial application is that instead of calling the series of functions in one go, you can call it in steps to create more specific functions. This helps make your functions more composable.