30 October 2007

Currying in JavaScript and Groovy

In the Groovy User Guide, the section "Fixing Closure Arguments to Constant Values Via Currying" gives a quick example of currying using Groovy closures:
def c = { arg1, arg2-> println "${arg1} ${arg2}" }
def d = c.curry("foo")
d("bar")
Basically, you bind one argument to a function taking multiple arguments but don't call the function until the remaining arguments are passed in.
Currying in JavaScript is a do-it-yourself affair. For references see these blog entries (in no particular order): "Curried JavaScript functions", "currying in javascript", "JavaScript currying". These are somewhat verbose and invasive.
The best option is to use prototype 1.6.0's curry function added to Function.prototype:
function sum(a, b) {
    return a + b;
}

sum(10, 5) // 15
var addTen = sum.curry(10);
addTen(5)  // 15
If you don't want to adopt prototype, you can use the following implementation:
function curry(f, args) {

    var thisF = f;
    var thisArgs = Array.prototype.slice.apply(args);

    return function(args) {
        thisArgs = thisArgs.concat(args);
        if (thisF.length <= thisArgs.length) {
            return thisF.apply(thisF, thisArgs);
        } else {
            return this;
        }
    }

}
With the canonical summation test:
function sum(a, b, c) {
    return a + b + c;
}

function test() {

    sum(1, 2, 3); // 6
    x = curry(sum, [1]);
    x([2]);
    x([3]); // 6
    x = curry(sum, [1]);
    x([2, 3]); // 6
			
}
This has not been put through a lot of testing, so YMMV. After looking at the other examples provided in the blogs above, I was astounded how little code it took me to write. As I test more and across multiple browsers, I'm sure it will start bloating...

[ updated 17 Jun 2009 ]

Cleaner implementation from #Javascript on IRC:

Function.prototype.curry = function() {
  var func = this, a = Array.prototype.slice.call(arguments, 0);
  return function() {
    return func.apply(this, a.concat(Array.prototype.slice.call(arguments, 0)));
  }
};
[ posted by sstrader on 30 October 2007 at 11:07:30 AM in Programming ]