What is Function Currying in JavaScript and When to Use It

thumbnail for What is Function Currying in JavaScript and When to Use It

Currying is a way of building functions in a way that it is possible to call them with arguments one by one, instead of all of them at once. It will allow us to create new functions, which have some arguments already remembered. Currying helps with creating specialized functions based on general ones, ex. getName, findId, joinWithSpaces from get, find, join. It helps reducing code duplication.

Creating a curried function

Let me start with very simple example, sum function in curried form:

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

// using arrow function

const sum = a => b => a + b;

Looks strange at first sight, but let me explain it.

After calling sum(5) it won't return a number, but a new function:

function(b) {
	return 5 + b; // a was changed to 5
}

// using arrow function

b => 5 + b;

Only after we call it the second time we will get the final result - sum of two numbers.

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

// using arrow function

const sum = a => b => a + b;

const addTo5 = sum(5); // function(b) { return 5 + b }

addTo5(3); // 8
// is the same as
add(5)(3); // 8

Summing up, we created specialized function addTo5 which has only one purpose - adding some number to 5. Function sum is more general.

When to use the curried function

Let's see another curried function, which you might use in your application. Function get will take key name, return a function which takes object and in the end return a value for provided key. One use-case if using it as an argument for Array.map

function get(key) {
  return function(obj) {
    return obj[key];
  }
}

// using arrow function

const get = key => obj => obj[key];

const getName = get('name');

const users = [{ name: 'John' }, { name: 'Emma' }];

const namesList = users.map(user => user.name); // normal way
const namesList = users.map(getName); // using specialized function
const namesList = users.map(get('name')) // using general function

As you can see this example is more practical. Because object is the last argument, we can pass this "unfinished" function to map, which calls this function with object. As a result we get value of name from the object.

Notice how more readable is this example when using get function - users.map(getName), you immediately understand what this code fragment does.

Good practices of creating curried functions

When creating your own curried function you should follow one important rule.

Always leave the source of data as the last argument. To determine the order of other "configuration" arguments, think about function use-cases.

Let's take a look at the get function where source of data (object) is NOT the last argument.

function get(obj) {
  return function(key) {
    return obj[key];
  }
}

// using arrow function

const get = obj => key => obj[key];

const users = [{ name: 'Adam' }, { name: 'Maciej' }]

const namesList = users.map(user => get(user)('name'))

As you can see using get function in this form doesn't make sense. And you can't really create a specialized function since you don't have access to the object yet. It is a good practice to leave the source of data as the last argument, because looking at the use-cases it usually comes at the end when executing the code.

Creating curried functions from standard function

In JavaScript world we are not used to curried functions, but looking at the use-cases it looks very promising. Actually we can create a function which is both curried and not.

function add(...args) {
  if (args.length === 1) {
    const [a] = args
    return function(b) {
      return a + b
    }
  }
  const [a, b] = args
  return a + b
}

add(5, 3) // 8
add(5)(3) // 8

If add is called with one argument args.length === 1, then we return new function where first argument is remembered, just like we did before. But if we call it with two arguments, it will just add them and return a sum.

You have to admit that it is very impressive, but the definition of this function is very complicated now and it's only summing up two numbers.

Thankfully there is a helper function which will help us with it:

function curry(argsLength, originalFunction) {
  function next(prevArgs) {
    function curriedFunction(nextArgs) {
      const allArgs = [...prevArgs, ...nextArgs]
      if (allArgs.length >= argsLength) {
        // all arguments are provided, call the function
        return originalFunction(...args);
      }
      else {
        return next(allArgs)
      }
    }
  }
  return next([])
}

It looks even more complicated but with it, we can simply transform the standard function into a curried one. Just remember that the first argument of curry is the number of arguments it can take, and the second one is the function itself;

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

const addTo5 = add(5);

add(5, 3) // 8
addTo5(3); // 8
add(5)(3); // 8

Now you don't have to worry about how many arguments you call the function with. You call the function like you did before you got to know about currying or call the function, by one argument at a time.

You might also like

thumbnail for Arrow functions in JavaScript: Everything you need to know about them
January 6, 2021
Arrow functions in JavaScript: Everything you need to know about them
Arrow function is an alternative form of creating a function in JavaScript, which were introduced in ES6.
thumbnail for Tailwind CSS: The future of styling or just another CSS framework?
January 15, 2021
Tailwind CSS: The future of styling or just another CSS framework?
Tailwind CSS looked like the first framework I would enjoy using. I already saw how easily you can create beautiful UIs with it (thanks to Tailwind UI project).
thumbnail for My 2020 In Review: What I Learned and Accomplished as Senior Front-end Developer
December 28, 2020
My 2020 In Review: What I Learned and Accomplished as Senior Front-end Developer
With 2020 coming to an end (finally!) I decided to do a little summary of things I learned during this year.

Hi friend,

Over the years working as a Front-end Developer in numerous projects I have gained a lot of experience. I've seen how choice of the right technology and architecture can affect the application and team behind it in a long run.

On this blog I want to share my experiance. I will show you solutions I discovered over the years. I want to teach you how to create better code and be successful developer.

Join the list of developers interested in web development

After signing up you will receive for free: Interview questions I ask as technical recruiter

newsletter