JavaScript Currying Redux

March 10, 2007

Currying in JavaScript is all the rage. There’s no shortage of code examples on how to do it. Regardless, currying is still a topic that many of us have a hard time truly wrapping our brains around. Let’s dig a little deeper. When we come out on the other side, we’ll have a little syntactic sugar for automagically currying functions that most of us have never seen before.

Why Should I Use Curry?

If you’re not already versed in the art of currying, you’re probably wondering why you should even bother with this technique. The main benefit of curried functions in JavaScript is for callbacks. Callbacks are used everywhere in JavaScript: event handlers, timeout handlers, Ajax oncomplete callbacks, and the very handy Array Extras methods map, filter and forEach added in JavaScript 1.6 by Mozilla. It’s often very useful to create a new function to use as a callback. Often these functions will never be used again, so they’re usually anonymous functions.

if (typeof currying == “undefined”)

The definition of currying is often vague and confusing, but the simplest explanation is this: Turning a function of two arguments into a function of one argument that returns a function of one argument. This definition comes from Higher-Order Perl by Mark Jason Dominus (MJD), a wonderful book on functional programming techniques that will change the way you think about code. The definition I just gave applies to functions of more than two arguments, but this is the simplest case. A simple example of manual currying:

// A normal version of add()
function add(x, y) { return x + y }

// Manually curried add()
function curried_add(x) {
    return function (y) {
        return x + y;
    };
}

add(1, 99);    // 100
curried_add(1)(99);    // 100

When we call curried_add() we only pass one argument. We get back another function that expects one argument, then uses the two arguments it’s received to perform its calculation.

In Higher-Order Perl, MJD shows a technique that lets you have your cake and eat it too. Using our original add() function, we can write it so that if we give it 2 arguments, it behaves as normal. But if we give it only one argument, it returns a curried function with that first argument filled in.

function add(x, y) {
    function curried(y) {
        return x + y;
    }
    return (arguments.length > 1) ? curried(y) : curried;
}

add(1, 99);    // 100
add(1)(99);    // 100

In this version of add(), we create an inner function that contains the actual function calculation. This inner function has access to the x argument of the outer function. If the add() function has been called with more than one argument, we’ll go ahead and invoke this nested function to compute the result. If only one argument has been supplied, we’ll return a function that will wait for the second argument before computing its result.

Automagical Currying

Writing this code over and over again will get tedious and be error-prone, so we’ll once again look to Higher-Order Perl for a generic solution. MJD gives us curry_n(), a function that generates a generic curried version of another function. Here it is in JavaScript:

function curry_n(f, n) {
    if (typeof n == 'undefined') n = f.length;
    return function () {
        if (arguments.length >= n) {
            return f.apply(this, arguments);
        } else {
            var a = [].slice.call(arguments,0);
            return curry_n(
                function () {
                    return f.apply(this, a.concat([].slice.call(arguments,0)))
                },
                n-a.length
            );
        }
    };
}

How does this work? It does essentially what we just did in our manually curried add(), but it’s generic. We pass a function to curry_n(). We get back a function that will check to see if it was called with the number of expected arguments. If it has, the function will return as normal. Otherwise, we’ll remember the arguments we did get, and then pass along a new function that will wait for the remaining arguments.

Now let’s return to our add() example. We’ll create a generic curried version that will work just like the manual one we created above:

var add = curry_n( function (x, y) { return x + y } );

add(1, 99);    // 100
add(1)(99);    // 100

var plus_one = add(1);
plus_one(10);    // 11
plus_one(99);    // 100

If you have your own Array extras file that uses Array.map(), this technique will come in especially handy:

[1,2,3,4,5].map(add(1));    // [2,3,4,5,6]

The curry_n() function takes an optional second parameter, which is the number of arguments your function expects. If you omit this, then the function’s length property will be used, which is the number of arguments you explicitly define during its creation. This will pose a problem for functions with explicitly defined optional arguments. In these cases, you’ll want to pass the second argument to curry_n() to include only the number of required arguments.

var map = curry_n( function (f, a, context) { return a.map(f, context) }, 2 );
Array.double = map( function (x) { return x * 2 } );
Array.double([1,2,3,4,5]);    // [2,4,6,8,10]

Because we’ve supplied the number 2 as the second argument to curry_n(), our function will return once it’s accumulated at least 2 arguments. We could still pass in the last optional argument when we make the call to Array.double() (though in this case it would be useless). If we omitted this argument, our function would not have executed until it received 3 arguments.

Caveats

I should note that this technique is not a replacement for other currying methods, such as Prototype’s Function.bind(), or even a generic curry function such as:

function curry(f) {
    var args = [].slice.call(arguments,1);
    return function () {
        return f.apply(this, args.concat([].slice.call(arguments,0)));
    };
}

What’s the difference? The difference is that curry_n() will return a function if not enough arguments have been provided, but will return the result of the function application if all its arguments have been passed. Meanwhile, curry() will always return a function, even if all arguments have been supplied. This comes into play for event callbacks or setTimeout() code. Sometimes in these situations you’ll have a function that you only call for its side effects, that you want to apply arguments to ahead of time. You might need to fill all arguments in, but you always need a function returned in these cases so it can be run by the event handler or timeout code.

curry_n() is meant strictly as a way to add implicit currying behavior to a defined function. It’s basically a form of polymorphism, or a very specific application of function overloading.

Resources:

Possibly Related:

  • Modify Existing JavaScript Functions
  • Extending DOM Nodes with Mootools
  • Useful Utility Functions in mootools
  • Extending Objects and Classes with mootools
  • Using Prototype’s New String Functions
  • 2 Responses to “JavaScript Currying Redux”

    1. Functional JavaScript at Oliver Steele Says:

      […] I bummed the +[].slice.call(arguments, 0)+ idiom from http://www.coryhudson.com/blog/2007/03/10/javascript-currying-redux, and used it all over the place to avoid an iteration or non-native function call. The ECMAScript Language Specification is careful to define slice in such a way that this works, but I don’t think I would have thought of it. […]

    2. Daily misery » Blog Archive » Links for 8.16.2007 through 8.21.2007 Says:

      […] JavaScript Currying Redux | CoryHudson.com […]