Here's my latest curry code. It allows for non-sequential partial application and point-free code (extraneous arguments are propagated to result).
It will not work properly with varargs because the point-free feature would require backtracking, which has two problems:
1) JS is not referentially transparent.
2) Recursive curried functions could take non-polynomial in time. (Or at least I couldn't figure out a way to do it in polynomial time.)
Should a function have varargs, it will treat the function as if it did not have any.
Function.freeArg = {
toString: function() {
return "[object FreeArg]"
}
};
var curry = (function() {
var curryApply = function(f, context, args) {
args = Array.prototype.slice.call(args);
while(args.length > 0 && typeof f == "function")
f = f.apply(context, args.splice(0, f.length));
return f;
};
var curry = function(f /*[, arity [, initArgs]]*/) {
var initArgs = arguments[2] || [];
var arity = typeof arguments[1] == "number"
? arguments[1]
: f.length
;
return arity > 0
? function(forceArityTo1) {
if(!arguments.length)
return arguments.callee;
var A = Array.prototype;
var args = initArgs.slice();
var i;
while(i = args.indexOf(Function.freeArg, i + 1), i != -1 && arguments.length)
args[i] = A.shift.call(arguments);
A.push.apply(args, arguments);
if(args.length < arity || args.indexOf(Function.freeArg) != -1)
return curry(f, arity, args);
return curryApply(f, this, args);
}
: function() {
return curryApply(f, this, arguments);
}
;
};
return curry;
})();
Examples:
var _ = Function.freeArg;
var sub = curry(function(a, b) {
return a - b;
});
var oneMinus = sub(1);
var dec = sub(_, 1);
alert(oneMinus(5)); // -1
alert(dec(5)); // 4
//-------------------------------------------------------//
Function.prototype.o = curry(function(g) {
var f = this;
return curry(function(x) {
return f(g(x));
});
});
var foldr = curry(function(f, z, xs) {
return !xs.length
? z
: f(xs[0], foldr(f, z, xs.slice(1)))
;
});
var cons = curry(function(x, xs) {
xs = xs.slice();
xs.unshift(x);
return xs;
});
var map = curry(function(f) {
return foldr(cons. o (f), []);
});
alert(map(dec, [1, 2, 3])); // [0, 1, 2]
//-------------------------------------------------------//
// Perhaps ensureArity should be moved in the main curry module.
var ensureArity = curry(function(arity, f) {
if(arity < 1)
return function() {
return f.call(this);
};
var supplyOneArg = curry(function(accumArgs, arg) {
accumArgs = accumArgs.concat([arg]);
return accumArgs.length < arity
? supplyOneArg(accumArgs)
: f.apply(this, accumArgs)
;
});
return supplyOneArg([]);
});
var reorder = curry(function(order, f) {
return ensureArity(order.length, function() {
var args = arguments;
args = map(function(idx) {
return args[idx];
}, order);
return f.apply(this, args);
});
});
var flip = reorder([1, 0]);
alert(flip(sub, 1, 2)); // 1
alert(flip(sub)(1, 2)); // 1
alert(flip(sub, 1)(2)); // 1
alert(flip(sub)(1)(2)); // 1