Currying

In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. For example, a function that takes two arguments, one from X and one from Y, and produces outputs in Z, by currying is translated into a function that takes a single argument from X and produces as outputs functions from Y to Z. Currying is related to, but not the same as, partial application.

For instance, Currying is translating a function from callable as f(a, b, c) into callable as f(a)(b)(c). Here is an example of how to implement curry in JS:

function curry(func) {
  return function(a) {
    return function(b) {
      return func(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let carriedSum = curry(sum);

alert( carriedSum(1)(2) ); // 3

As you can see, the implementation is a series of wrappers.

In other words, when a function, instead of taking all arguments at one time, takes the first one and return a new function that takes the second one and returns a new function which takes the third one, and so forth, until all arguments have been fulfilled.

That is, when we turn a function call add(1,2,3) into add(1)(2)(3) . By using this technique, the little piece can be configured and reused with ease.

Why it’s useful ?

Let’s look at a simple add function. It accepts three operands as arguments, and returns the sum of all three as the result.

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

You can call it with too few (with odd results), or too many (excess arguments get ignored).

add(1,2,3) --> 6 
add(1,2) --> NaN
add(1,2,3,4) --> 6 //Extra parameters will be ignored.

How to convert an existing function to curried version?

The curry function does not exist in native JavaScript. But library like lodash makes it easier to convert a function to curried one.

//import or load lodash
var abc = function(a, b, c) {
  return a + b + c;
};
 
var curried = _.curry(abc);
var addBy2 = curried(2);
console.log(addBy2(0,0));
// => 2
console.log(addBy2(1,1));
// => 4
console.log(curried(4)(5)(6));
// => 15

Example:Logging

Advanced currying allows both to keep the function callable normally and to get partials easily. To understand the benefits we definitely need a worthy real-life example. For instance, we have the logging function log(date, importance, message) that formats and outputs the information. In real projects such functions also have many other useful features like: sending it over the network or filtering:

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

Let’s curry it!

log = _.curry(log);

After that log still works the normal way:

log(new Date(), "DEBUG", "some debug");

…But also can be called in the curried form:

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

Let’s get a convenience function for today’s logs:

// todayLog will be the partial of log with fixed first argument
let todayLog = log(new Date());

// use it
todayLog("INFO", "message"); // [HH:mm] INFO message

And now a convenience function for today’s debug messages:

let todayDebug = todayLog("DEBUG");

todayDebug("message"); // [HH:mm] DEBUG message

So:

Advanced curry implementation

In case you’re interested, here’s the “advanced” curry implementation that we could use above.

function curry(func) {

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

}

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

let curriedSum = curry(sum);

// still callable normally
alert( curriedSum(1, 2, 3) ); // 6

// get the partial with curried(1) and call it with 2 other arguments
alert( curriedSum(1)(2,3) ); // 6

// full curried form
alert( curriedSum(1)(2)(3) ); // 6

The new curry may look complicated, but it’s actually pretty easy to understand.

The result of curry(func) is the wrapper curried that looks like this:

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

When we run it, there are two branches: