DEV Community

loading...

What is Function Currying in JavaScript and When to Use It

slawomirkolodziej profile image Sławek Kołodziej Originally published at slawkolodziej.com Updated on ・4 min read

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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([])
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.


I'm regularly publishing my insights on web development.
Consider subscribing to my newsletter.
Visit my blog at slawkolodziej.com to find out more interesting content.
Follow me on Twitter.

Discussion (4)

pic
Editor guide
Collapse
sarneeh profile image
Jakub Sarnowski

Awesome article! I'd add one thing: there are amazing libraries out there with a lot of useful functions ready to be used out of the box, for example ramda.

Collapse
slawomirkolodziej profile image
Sławek Kołodziej Author

Yes! Ramda functions are curried out of the box.

Collapse
ashvin777 profile image
Ashvin Kumar Suthar

Great snippets ! Do you mind creating these snippets in JSitor.com 🙂

It will be helpful to see live version of these snippets.

Collapse
danwalsh profile image
Dan Walsh

Very interesting!