Homemade Curry

Lots of "functional programming utility libraries" (LoDash, Ramda, et. all) have a curry method.

curry is quite powerful, as it allows you to build out a function in pieces, defining behavior along the way. This in turn can allow for a flatter source code structure in something known as a points-free style.

While it's useful to lean on libs, It's equally useful (if not more so) to understand how to implement these ideas yourself. In that vein, here is a simple annotated version of curry:

// First we will save off an array conversion function, by pulling
// slice off of Array.prototype and binding it back. This assumes
// an ES5+ environment. This allows the array-like arguments object
// to be converted to an array.
var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);

// Here is our curry function declaration.
// We will ask for an arity (number of arguments to satisfy)
// and the function to curry. Because we want to work
// in a points-free style, this will *always* return
// a function back. We could use fn.length to determine
// arity, but we actually gain more clarity and
// flexibility by having the user specify it
// when creating a curried function.
function curry (arity, fn) {  
  return function innerCurry () {
    // convert incoming arguments to an array.
    // This will be closed over by buildArgs below.
    // We slice to arity because we don't want
    // more args than we specify making it to fn.
    var args = toArray(arguments, 0, arity);

    // The main logic of our curry function.
    // This will call fn if all args are satisfied.
    // Otherwise, this will return buildArgs.
    return args.length === arity ? fn.apply(null, args) : buildArgs;

    // buildArgs is returned in the case where our 
    // arity is not satisfied.
    function buildArgs () {
      // When buildArgs is invoked, we combine previous args
      // with the rest of the args passed in. This allows for
      // branching curried functions as total arguments are distributed 
      // across calls, and not stored in a central location.
      var allArgs = args.concat(toArray(arguments));

      // Call innerCurry again with our total args.
      // This will either return another innerCurry,
      // or the result of fn if all args are satisfied.
      return innerCurry.apply(null, allArgs);
    }
  }
}

This simple implementation is 11 SLOC, and uses no external libs. Let's see how we can use curry to do some useful work. We will create simple functions that are utilized by ES5 array methods. You will see the points-free style in that we are able to pass curried functions into the Array methods; there are no inline function declarations needed. ES5 array methods pass extra args to each call (index, and the array being iterated). With our curry implementation limiting the upper bound of args to arity, we don't have to worry about fn being called with extra args in this case.

  var adder = curry(2, function adder (a, b) {
    return a + b;
  });
  var add1 = adder(1);

  [1, 2, 3].map(add1);
  // [2, 3, 4]

  var gt = curry(2, function gt (a, b) {
    return b > a;  
  });
  var gt2 = gt(2);

  [2, 3, 4].filter(gt2);
  // [3, 4]

These are simple examples. In the next post, we will explore the power of composing these curried functions, building up complex behaviors from simple functional building blocks. The power of functional programming will come into play as we write stateless code that is easy to reason about and can be viewed in simple isolated pieces.