Trinithis
05-01-2008, 01:04 AM
This is a generalized curry function. It does normal currying plus a bit more.
As you would expect, the curried function supports sequential partial application. However, This 'curry' function also allows for non-sequential partial application. It also allows the currying of variable argument functions.
Curry function:
Function.freeArg = {
toString: function() {
return "[object FreeArg]";
}
};
function curry(f /*, n [, a] */) {
return f.curry(arguments[1], arguments[2]);
}
Function.prototype.curry = (function() {
var toString = function() {
return String(this.valueOf());
};
return function(/* n [, a] */) {
var f = this;
var a = arguments[1] || [];
var n = typeof arguments[0] == "number"
? arguments[0]
: f.length;
return function() {
if(!arguments.length)
return arguments.callee;
var p = a.slice();
var i = -1;
while(i = p.indexOf(Function.freeArg, i + 1), i != -1)
p[i] = p.shift.call(arguments);
p.push.apply(p, arguments);
if(n < 0) {
var g = f.curry(n, p);
g.valueOf = function() {
return f.apply(null, p);
};
g.toString = toString;
return g;
}
if(p.length < n || p.indexOf(Function.freeArg) != -1)
return f.curry(n, p);
return f.apply(null, p);
};
};
})();
if(!Array.prototype.indexOf)
Array.prototype.indexOf = function(v, i) {
var n = this.length;
if((i |= 0) < 0) {
i += n;
if(i < 0)
i = 0;
}
for(; i < n; ++i)
if(this[i] === v)
return i;
return -1;
};
With non-sequential partial application, supply Function.freeArg in the argument to be temporarily bypassed. In my example, I declare _ to equal Function.freeArg to give the code a more functional feel.
With variable arg currying (mandatory if you repeatedly do partial application, but otherwise not), when you curry the function, you need to specify the number of arguments to curry as a negative number, such as -1. Normally the curry function infers the number of arguments to curry as the default function argument length. In the case that you do supply the negative value length argument, any application of the returned function always return a function that always returns a function, etc. To get the value of the function, you need to force its value either explicitly or implicitly via the toString or valueOf methods of the function.
Example code:
var _ = Function.freeArg;
-------------------------------------
// a curried add function
var add = curry(function(x, y) {
return x + y;
});
var inc = add(1);
-------------------------------------
// a curried sub function
var sub = curry(function(x, y) {
return x - y;
});
var oneMinus = sub(1);
var dec = sub(_, 1);
-------------------------------------
// a variable argument average function
var average = curry(function() {
var sum = 0;
for(var i = 0; i < arguments.length; ++i) {
sum += arguments[i];
}
return sum / arguments.length;
}, -1); // note the -1 argument!
var avg_4_7_args = average(4, 7);
var avg_4_7_0_args = avg_4_7_args(0);
var avg_1_2_3_args = average(1)(2)(3);
-------------------------------------
var alert_x_y_z_w = function(x, y, z, w) {
alert([x, y, z, w].join('\n'));
}.curry(); // OO-style
var alert_1_y_2_w = alert_x_y_z_w(1, _, 2, _);
-------------------------------------
var x = 5;
alert(inc(x));
alert(oneMinus(x));
alert(dec(x));
alert(avg_4_7_args(1, 2, 3));
alert(avg_4_7_0_args(1,2)(3)(4,5,6));
alert(avg_1_2_3_args());
alert_1_y_2_w(3, 4);
The example code doesn't show much or explain much, but what it does demonstrate is the flexibility of how you can call the curried function. To understand what currying is, you can try:
- http://ianhenderson.org/currying_in_javascript.html
- http://www.google.com/search?hl=en&q=currying&btnG=Google+Search
In the short, a curried function allows you to pass one argument at a time to a function, and that will return a new function if there are arguments that you "still need to pass" into the function.
As you would expect, the curried function supports sequential partial application. However, This 'curry' function also allows for non-sequential partial application. It also allows the currying of variable argument functions.
Curry function:
Function.freeArg = {
toString: function() {
return "[object FreeArg]";
}
};
function curry(f /*, n [, a] */) {
return f.curry(arguments[1], arguments[2]);
}
Function.prototype.curry = (function() {
var toString = function() {
return String(this.valueOf());
};
return function(/* n [, a] */) {
var f = this;
var a = arguments[1] || [];
var n = typeof arguments[0] == "number"
? arguments[0]
: f.length;
return function() {
if(!arguments.length)
return arguments.callee;
var p = a.slice();
var i = -1;
while(i = p.indexOf(Function.freeArg, i + 1), i != -1)
p[i] = p.shift.call(arguments);
p.push.apply(p, arguments);
if(n < 0) {
var g = f.curry(n, p);
g.valueOf = function() {
return f.apply(null, p);
};
g.toString = toString;
return g;
}
if(p.length < n || p.indexOf(Function.freeArg) != -1)
return f.curry(n, p);
return f.apply(null, p);
};
};
})();
if(!Array.prototype.indexOf)
Array.prototype.indexOf = function(v, i) {
var n = this.length;
if((i |= 0) < 0) {
i += n;
if(i < 0)
i = 0;
}
for(; i < n; ++i)
if(this[i] === v)
return i;
return -1;
};
With non-sequential partial application, supply Function.freeArg in the argument to be temporarily bypassed. In my example, I declare _ to equal Function.freeArg to give the code a more functional feel.
With variable arg currying (mandatory if you repeatedly do partial application, but otherwise not), when you curry the function, you need to specify the number of arguments to curry as a negative number, such as -1. Normally the curry function infers the number of arguments to curry as the default function argument length. In the case that you do supply the negative value length argument, any application of the returned function always return a function that always returns a function, etc. To get the value of the function, you need to force its value either explicitly or implicitly via the toString or valueOf methods of the function.
Example code:
var _ = Function.freeArg;
-------------------------------------
// a curried add function
var add = curry(function(x, y) {
return x + y;
});
var inc = add(1);
-------------------------------------
// a curried sub function
var sub = curry(function(x, y) {
return x - y;
});
var oneMinus = sub(1);
var dec = sub(_, 1);
-------------------------------------
// a variable argument average function
var average = curry(function() {
var sum = 0;
for(var i = 0; i < arguments.length; ++i) {
sum += arguments[i];
}
return sum / arguments.length;
}, -1); // note the -1 argument!
var avg_4_7_args = average(4, 7);
var avg_4_7_0_args = avg_4_7_args(0);
var avg_1_2_3_args = average(1)(2)(3);
-------------------------------------
var alert_x_y_z_w = function(x, y, z, w) {
alert([x, y, z, w].join('\n'));
}.curry(); // OO-style
var alert_1_y_2_w = alert_x_y_z_w(1, _, 2, _);
-------------------------------------
var x = 5;
alert(inc(x));
alert(oneMinus(x));
alert(dec(x));
alert(avg_4_7_args(1, 2, 3));
alert(avg_4_7_0_args(1,2)(3)(4,5,6));
alert(avg_1_2_3_args());
alert_1_y_2_w(3, 4);
The example code doesn't show much or explain much, but what it does demonstrate is the flexibility of how you can call the curried function. To understand what currying is, you can try:
- http://ianhenderson.org/currying_in_javascript.html
- http://www.google.com/search?hl=en&q=currying&btnG=Google+Search
In the short, a curried function allows you to pass one argument at a time to a function, and that will return a new function if there are arguments that you "still need to pass" into the function.