Twey
08-02-2007, 01:34 AM
I don't know how many of you are aware that ECMAScript can be used as a functional language (I suspect the definition of a functional language (http://en.wikipedia.org/wiki/Functional_programming) would probably increase that number), but I've had a fair few of people asking me on IRC how currying is accomplished in Javascript, so I thought I'd share it here in the hopes that it might reach a few of them, and maybe useful to others as well:
function curry(f /* [ , l [, a]] */) {
var l = (typeof arguments[1] === "number" ? arguments[1] : (f.length || 0)),
a = arguments[2] || [];
return function() {
var m = a.concat(Array.prototype.slice.call(arguments));
if(m.length < l)
return curry(f, l, m);
else
return f.apply(this, m);
};
}The function takes three arguments, the latter two of which are optional: the function to curry, the number of arguments to take before applying them to the function, and the arguments to store initially.
Currying allows arguments to be passed to a function one at a time, each applied argument returning either a function that can apply the next argument or a the result of the function with the given arguments, if enough arguments have been given. ECMAScript's variable arguments also allow, unusually, for a variable number of arguments to be passed to each function. For example, say we had a very simple function:
function add(a, b, c, d) {
return a + b + c + d;
}We can curry it like so:
var add6 = curry(add)(6);
add6(1, 2, 3); // returns 12 (6 + 1 + 2 + 3)
var add6and4 = add6(4);
add6and4(1, 3); // 14 (6 + 4 + 1 + 3)You can add as many arguments in each call as you like.
For a more complex example, how about a summing function that takes an arbitrary number of arguments:
function madd() {
var t = 0, i = 0;
for(; i < arguments.length; ++i)
t += arguments[i];
return t;
}We can do:
var c_madd = curry(madd);
c_madd(1);... but this returns 0 -- there are no formal parameters defined, so the function is called immediately. Attempts at further currying would, of course, fail with an error: currying a number doesn't make much sense! Instead, we can use the optional second argument:
curry(madd, 4)(1)(2)(3)(4); // 10 (1 + 2 + 3 + 4)The optional third argument is actually mostly there for internal use (it's used to pass on the current arguments for the next internal call to curry()) but it can sometimes be convenient to the user as well: curry(add, null, [1, 2, 3]) is the same as curry(add)(1, 2, 3).
Applied wisely, currying can greatly improve readability, flexibility, and code size of your scripts, since it can be used in a lot of situations where people would normally define a full-blown function.
function curry(f /* [ , l [, a]] */) {
var l = (typeof arguments[1] === "number" ? arguments[1] : (f.length || 0)),
a = arguments[2] || [];
return function() {
var m = a.concat(Array.prototype.slice.call(arguments));
if(m.length < l)
return curry(f, l, m);
else
return f.apply(this, m);
};
}The function takes three arguments, the latter two of which are optional: the function to curry, the number of arguments to take before applying them to the function, and the arguments to store initially.
Currying allows arguments to be passed to a function one at a time, each applied argument returning either a function that can apply the next argument or a the result of the function with the given arguments, if enough arguments have been given. ECMAScript's variable arguments also allow, unusually, for a variable number of arguments to be passed to each function. For example, say we had a very simple function:
function add(a, b, c, d) {
return a + b + c + d;
}We can curry it like so:
var add6 = curry(add)(6);
add6(1, 2, 3); // returns 12 (6 + 1 + 2 + 3)
var add6and4 = add6(4);
add6and4(1, 3); // 14 (6 + 4 + 1 + 3)You can add as many arguments in each call as you like.
For a more complex example, how about a summing function that takes an arbitrary number of arguments:
function madd() {
var t = 0, i = 0;
for(; i < arguments.length; ++i)
t += arguments[i];
return t;
}We can do:
var c_madd = curry(madd);
c_madd(1);... but this returns 0 -- there are no formal parameters defined, so the function is called immediately. Attempts at further currying would, of course, fail with an error: currying a number doesn't make much sense! Instead, we can use the optional second argument:
curry(madd, 4)(1)(2)(3)(4); // 10 (1 + 2 + 3 + 4)The optional third argument is actually mostly there for internal use (it's used to pass on the current arguments for the next internal call to curry()) but it can sometimes be convenient to the user as well: curry(add, null, [1, 2, 3]) is the same as curry(add)(1, 2, 3).
Applied wisely, currying can greatly improve readability, flexibility, and code size of your scripts, since it can be used in a lot of situations where people would normally define a full-blown function.