JavaScript Currying

In JavaScript, there exists an advanced technique of working with functions. It is called carrying. However, it is used not only in JavaScript but also in other programming languages.

Generally, it is a transformation of functions.So, it translates a function from callable likef(a, b, c) to f(a)(b)(c) .

A function can’t be called by currying. What it does is merely transforming.

For a better perception, let’s start at an example:

function curry(fn) { // curry(fn) does transforms curry
  return function (a) {
    return function (b) {
      return fn(a, b);
    };
  };
}
 
function sum(a, b) {
  return a + b;
}
let currySum = curry(sum);
console.log(currySum(10)(20)); // 30

In the example above, there is a helper function curry(fn) that implements currying for a two-argument f.

It’s a simple performance with two wrappers. A wrapper function(a) is the result of curry(func). Once you call curySum(1), the argument is saved in the Lexical Environment. A new wrapper function(b) is returned. Afterward, it is called with argument 2, passing the call to sum .

Here is a more complex example:

function sum(a, b) {
  return a + b;
}
let currySum = _.curry(sum); // using _.curry from lodash library
alert(currySum(10, 20)); // 30, still callable normally
alert(currySum(10)(20)); // 30, called partially
The purpose of Currying

To understand the main purpose of currying, we should check out a real-life example:

function log(date, importance, message) {
  console.log(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}

Here we have log(date, importance, message), which formats and outputs information.

Currying it will look like this:

logs = _.curry(log);

So, the log will work properly after that:

log(new Date(), "DEBUG", "debug"); // log(a, b, c)

In the curried form it will also work:

log(new Date())("DEBUG")("debug"); // log(a)(b)(c)

So, after currying, nothing is lost. Partial functions, also, can be easily generated.

Advanced Currying

Now let’s get into some details and check out more complicated currying with multi-argument functions.

It’s quite brief, as shown below:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}

A usage example will look like this:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}
function sum(a, b, c) {
  return a + b + c;
}
let currySum = curry(sum);
console.log(currySum(10, 20, 30)); // 60, still callable normally
console.log(currySum(10)(20, 30)); // 60, currying of 1st arg
console.log(currySum(10)(20)(30)); // 60, full currying

The wrapper curried will become the result of curry(fn):

// fn is function to transform
function curried(...args) {
  if (args.length >= fn.length) { // (1)
    return fn.apply(this, args);
  } else {
    return function pass(...args2) { // (2)
      return curried.apply(this, args.concat(args2));
    }
  }
};

Running it will lead to two if execution branches. The first is calling now. In case the passed args count is similar to the original function includes in its definition or longer, then the call should be passed to it. The second is getting a partial. In another way, the fn is not called yet. Another pass is returned. It can re-apply curried providing previous arguments with the new ones. Afterward, on a new call, either a new partial or the result will be received.

It is essential to note that for the currying, a fixed number of arguments is required. So, a function using rest parameters f(...args) like will not be curried like that.

Summary

In JavaScript, currying represents a transform, which turns the callable f(a,b,c) to f(a)(b)(c) . Normally, JavaScript implementations keep the function callable, as well as return the partial, once the argument counts are less than needed. Getting partials is also easy with currying.




Do you find this helpful?

Related articles