Gettin’ Freaky Functional w/Curried JavaScript

Posted on by in Web

Partial application refers to the practice of filling in a functions parameters with arguments, deferring others to be provided at a later time. JavaScript libraries like Underscore facilitate partial function application – but the API isn’t for everyone. I, for one, feel icky sprinkling calls to _.partial and _.bind throughout my application.

Curried functions (found in Haskell and supported by Scala, among others) can be partially-applied with little ceremony; no special call format is required. In this blog post, I’ll demonstrate an approach to currying JavaScript functions at the time of their definition in a way that enables partial function application without introducing lots of annoying parens and nested function expressions.

Currying in JavaScript: Usually a Huge Pain

A function can be said to have been “curried” if implemented as a series of evaluations of nested unary functions (representing each “parameter”) instead of as a single, multiple-arity function (currying a unary function isn’t all that interesting).

This binary function:

//
// (Number, Number) → Number
//
function sum(x, y) {
  return x + y;
}

…could be rewritten as a curried function like this:

//
// Number → Number → Number
//
function sum(x) {
  return function(y) {
    return x + y;
  };
}

var addTen = sum(10); // type: Number → Number

addTen(20); // 30
addTen(55); // 65

This approach starts to become unwieldy as the number of values to be provided by the caller grows:

//
//  type: Number → Number → Number → Number → Number
//
function sumFour(w) {
  return function (x) {
    return function (y) {
      return function (z) {
        return w + x + y + z;
      }
    }
  }
}

sumFour(1)(2)(10)(20); // 33

var addTen = sumFour(3)(3)(4);
addTen(20); // 30

Yuck. We get partial application – but with a very low signal-to-noise ratio. Four sets of parens are required to call sumFour – and its implementation requires three nested function expressions.

Higher-Order Currying for the Lazy

As luck may have it (thanks, Brendan Eich), we can write a higher-order currying function that can be applied to a fixed-arity function, returning a curried version of the original. Making things even sweeter, our curried function can be called like an idiomatic JavaScript function (one set of parens and comma-separated arguments) instead of like the sumFour mess above – all without losing the ability to perform low-ceremony partial application.

We’ll jump straight into the implementation:

function curry(fx) {
  var arity = fx.length;

  return function f1() {
    var args = Array.prototype.slice.call(arguments, 0);
    if (args.length >= arity) {
      return fx.apply(null, args);
    }
    else {
      return function f2() {
        var args2 = Array.prototype.slice.call(arguments, 0);
        return f1.apply(null, args.concat(args2)); 
      }
    }
  };
}

Calling curry with a multiple arity function produces a new function which, when called, either returns a partially applied version of the original or the result of full application. Leveraging Function.prototype.length, we can compare the number of provided arguments (which we cache in args after each function call) with the arity of the original. If we’ve received enough arguments to fully apply, the original function is called. If not, we recurse into our helper (f1)… returning a new, partially applied function.

Let’s see what it looks like to call this function – and how to interact with the resulting function.

var sumFour = curry(function(w, x, y, z) {
  return w + x + y + z;
});

The name sumFour is now bound to a curried function returned from our call to curry in which we passed an anonymous, four-parameter function expression. Calling the resulting function will either result in a value (the sum of all four numbers) or a new function with some of its parameters filled in with values provided by the caller.

var 
  f1 = sumFour(10),         // returns a function awaiting three arguments
  f2 = sumFour(1)(2, 3),    // returns a function awaiting one argument
  f3 = sumFour(1, 2, 7),    // returns a function awaiting one argument
  x  = sumFour(1, 2, 3, 4); // sumFour has been fully applied; x is equal to 1+2+3+4=10
  x2 = sumFour(1)(2)(3)(4); // fully applied; x2 equals 10

There are a few things of importance in this last example:

  1. Our curried function can be applied to multiple arguments without the need for multiple sets of parameters
  2. Our curried function can be partially applied with library support (beyond what was required to create the function bound to sumFour
  3. The result of partially applying our curried function to arguments results in a function that is also curried

Cool, right?

In hopes of illustrating how this crazy function works, let’s walk through the evaluation of the expression (sumFour(1)(2, 3, 4)):

  1. Evaluate the subexpression sumFour(1), which (you guessed it) applies sumFour to 1
  2. Call f1 with a single argument 1
  3. Check to see if f1 has been called with a number of arguments greater or equal to the number of parameters expressed in the anonymous function passed to curry (four: w, x, y, and z). The predicate returns false and f2 is returned.
  4. f2 is called with three arguments 2 and 3 and 4
  5. Concatenate the first argument 1 with new arguments and apply f1 to all four
  6. Check number of arguments (now 4) against arity of original function (4) which satisfies predicate. The original function is then (fully) applied to all four arguments.

In Summary

JavaScript implementations don’t make things easy for programmers that want to work with partially-applied and curried functions (lots of parens, nested function expressions). In this blog post, you’ve seen a demonstration of how one might roll a higher-order currying function that allows for partial function application without a ton of hassle.

Related Reads

  1. Composing Asynchronous with Synchronous Functions in JavaScript
  2. Tidying Up a JavaScript Application with Higher-Order Functions

Feedback

  Comments: 6


  1. Currying with curry() is a really cool trick, and I agree that wrapping functions with curry() has a lower signal to noise ratio than nested functions. But, when we talk about code style, we really need to talk about two types of signal: The one that that tells us what we’re trying to accomplish and the other that tells us how we’re trying accomplish it. When there’s disagreement on code style, it’s usually between people that favor one over the other. Both are really necessary to maintain code. If code errs on the side of the what (it’s too expressive) it reads like English, but acts like magic and is difficult to augment or debug. If it errs on the how (exposes too much of the mechanics), coders will miss the forest for the trees trying to figure out what the code is supposed to do.

    Seen through this lens, currying with nested functions is less expressive, but it is more clear in how the currying works and also in how the function author intends the function to be curried. I’ve been using nested function currying extensively in my side projects and have been really enjoying it.


    • The disadvantage of currying with nested functions is the lack of flexibility. With the `curry` call you can call any of these, depending on your needs:

      sumFour(1, 2, 3, 4);
      sumFour(1, 2, 3)(4);
      sumFour(1, 2)(3, 4);
      sumFour(1, 2)(3)(4);
      sumFour(1)(2, 3, 4);
      sumFour(1)(2, 3)(4);
      sumFour(1)(2)(3, 4);
      sumFour(1)(2)(3)(4);

      But with nested functions, only the last form would be available.

      • Aleksey Pavlichenko


        While I agree about this specific case, if we consider naming nested functions like this:

        function sumFour(w) {
        return {
        addx:function (x) {
        return function (y) {
        return function (z) {
        return w + x + y + z;
        }
        },
        addy:function (y) {
        return function (x) {
        return function (z) {
        return w + x + y + z;
        }
        }
        }
        }
        }
        then we have a choice of which argument to curry first, which we don’t have with function curry or bind:
        sumFour(1).addx(2)(3)(4)
        sumFour(1).addy(3)(2)(4)

        Of course, it matters only if addx and addy do different things, for example, addition and multiplication.


  2. Note that this style of currying is built into libraries like Ramda [1] (disclosure: I’m an author) and FKit [2].. Built in functions are curried like that, and similar `curry` functions are offered for your own use. Lodash is playing catch-up, I think, but has also just released a version on similar principles [3], although I’ve yet to look closely at it.

    An interesting point to note about currying is that it makes you think much more closely about parameter order. Because you can so easily partially apply the initial parameters, you want to put first the parameters least likely to change. Those most likely to change should come last. Thus where Underscore or lodash (proper — how are we going to refer to the regular one now, lodash-classic? 🙂 ) will order the parameters to `map` as `list` then `callback function`, if `map` is auto-curried, as with Ramda or FKit, it really should be `callback function`, then `list` so that you can do this:

    var square = function(x) {return x * x;};
    map(square, [1, 2, 3]); //=> [1, 4, 9]
    var squareAll = map(square); // since `map` is curried
    squareAll([1, 2, 3, 4, 5]); //=> [1, 4, 9, 16, 25]

    Currying also does not play so well with optional parameters. It’s especially bad if they’re in the middle, but even at the end of the parameter list, they can make it difficult to decide if all the parameters required have been supplied. It’s not impossible in all situations, but handling the general case might be too tricky to bother with.

    [1]: https://github.com/ramda/ramda
    [2]: https://github.com/nullobject/fkit
    [3]: https://github.com/lodash/lodash-fp


  3. In EcmaScript 2015 I would do it the other way around. First, predefine all functions under my control as curried functions. Arrows facilitate this task immensely and keep the code concise:

    const add = x => y => x + y;

    Now the already mentioned inflexibility comes into effect:

    add(2)(3); // cumbersome

    So we once again have to rely on a separate helper function:

    const uncurry = f => (…args) => args.reduce((g, x) => g(x), f);
    const add = uncurry(x => y => x + y);

    Now we reach the desired flexibility:

    let add2 = add(2);
    add2(3); // 5
    add(2, 3); // 5


    • Note that this is not as flexible as the version in the post, however.

      const sumFour = uncurry(w => x => y => z => w + x + y + z);

      sumFour(1, 2, 3, 4); //=> 10
      sumFour(1, 2, 3)(4); //=> 10
      sumFour(1, 2)(3)(3); //=> 10
      sumFour(1)(2)(3)(4); //=> 10

      // but

      sumFour(1, 2)(3, 4); //=> z => 1 + 2 + 3 + z
      sumFour(1)(2, 3, 4); //=> y => z => 1 + 2 + y + z

      // etc

Your feedback