Friday, February 1, 2008

Currying in Prototype

If you've spent much time in the functional or higher-order programming mindset, you've already gotten a feel for the expressive power that comes from the ability to compose new functions out of existing ones. In pure functional languages, one of the basic types of function composition operations is called "currying" (after Haskell Curry, an early pioneer in this particular space). When you curry a function, you essentially bind parameter values to it, and return a new function over the remaining parameters. Some languages have built-in support for currying as a fundamental operation (ML and Haskell, for example), but you can implement them in any language that supports closures and functions as first-class objects. The Prototype library, as of version 1.6, does this for JavaScript via the Function.prototype.curry method.

Here it is, in all its beautiful compactness (without the Object.extend confusion):
Function.prototype.curry = function() {
if (!arguments.length) return this;
var __method = this, args = $A(arguments);
return function() {
return __method.apply(this, args.concat($A(arguments)));
}
};
We'll walk through line by line:
if (!arguments.length) return this;
If no arguments were bound, simply return the function as-is.
var __method = this, args = $A(arguments);
Store the original function in a closure as __method, and the arguments to be bound as args.
return function() { ... }
Nothing surprising here. We're going to return a new function.
return __method.apply(this, args.concat($A(arguments)));
This is where the magic happens. The newly created function, when called, is going to return the result of calling the original function -- __method.apply(this, ...) -- with the original arguments -- args -- prepended to the invocation arguments.

An illustration is worth a thousand words:
function original(x, y, z) {
return x+y+z;
}

var bound_x = original.curry(10);
bound_x(20, 30) // ==> original(10, 20, 30)
// ==> 60

// sure, you can curry a curried function
var bound_x_y = bound_x.curry(20);
bound_x_y(30) // ==> bound_x(20, 30)
// ==> original(10, 20, 30)
// ==> 60

var bound_x = original.bind(this, 10);
var bound_x_y = bound_x.bind(this, 20);

Labels:

1 Comments:

At February 15, 2008 4:56 PM , Blogger Susan said...

Fred: Thanks for the post on Currying in Prototype. It looks like Matz of Ruby fame, also thought currying was useful. Support now exists as an instance method of the Ruby standard library Proc class. It was added to the Ruby 1.9 branch after the 1.9.0 release as an easter egg.

One should be able to do the following:

sum = lambda {|x, y, z| (x + y + z); }

x_bound = sum.curry(10)

puts x_bound(9, 8) #=> 27

Note: I remember seeing examples of variable arity methods in JavaScript a while back and would love to see a short blog article by you on this, if you have time.

 

Post a Comment

<< Home