(function() {
var slice = Array.prototype.slice;
var toArray = function(a){ return slice.call(a) }
var tail = function(a){ return slice.call(a, 1) }
// fn, [value] -> fn
//-- create a curried function, incorporating any number of
//-- pre-existing arguments (e.g. if you're further currying a function).
var createFn = function(fn, args, totalArity){
var remainingArity = totalArity - args.length;
var ctx = this;
switch (remainingArity) {
case 0: return function(){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 1: return function(a){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 2: return function(a,b){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 3: return function(a,b,c){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 4: return function(a,b,c,d){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 5: return function(a,b,c,d,e){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 6: return function(a,b,c,d,e,f){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 7: return function(a,b,c,d,e,f,g){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 8: return function(a,b,c,d,e,f,g,h){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 9: return function(a,b,c,d,e,f,g,h,i){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
case 10: return function(a,b,c,d,e,f,g,h,i,j){ return processInvocation.call(this, fn, concatArgs(args, arguments), totalArity) };
default: return createEvalFn(fn, args, remainingArity);
}
}
// [value], arguments -> [value]
//-- concat new arguments onto old arguments array
var concatArgs = function(args1, args2){
return args1.concat(toArray(args2));
}
// fn, [value], int -> fn
//-- create a function of the correct arity by the use of eval,
//-- so that curry can handle functions of any arity
var createEvalFn = function(fn, args, arity){
var argList = makeArgList(arity);
//-- hack for IE's faulty eval parsing -- http://stackoverflow.com/a/6807726
var fnStr = 'false||' +
'function(' + argList + '){ return processInvocation.call(this, fn, concatArgs(args, arguments)); }';
return eval(fnStr);
}
var makeArgList = function(len){
var a = [];
for ( var i = 0; i < len; i += 1 ) a.push('a' + i.toString());
return a.join(',');
}
var trimArrLength = function(arr, length){
if ( arr.length > length ) return arr.slice(0, length);
else return arr;
}
// fn, [value] -> value
//-- handle a function being invoked.
//-- if the arg list is long enough, the function will be called
//-- otherwise, a new curried version is created.
var processInvocation = function(fn, argsArr, totalArity){
argsArr = trimArrLength(argsArr, totalArity);
if ( argsArr.length === totalArity ) return fn.apply(this, argsArr);
return createFn.call(this, fn, argsArr, totalArity);
}
// fn -> fn
//-- curries a function! <3
var curry = function(fn){
return createFn.call(this, fn, [], fn.length);
}
// num, fn -> fn
//-- curries a function to a certain arity! <33
curry.to = curry(function(arity, fn){
return createFn.call(this, fn, [], arity);
});
// num, fn -> fn
//-- adapts a function in the context-first style
//-- to a curried version. <3333
curry.adaptTo = curry(function(num, fn){
return curry.to.call(this, num, function(context){
var args = tail(arguments).concat(context);
return fn.apply(this, args);
});
})
// fn -> fn
//-- adapts a function in the context-first style to
//-- a curried version. <333
curry.adapt = function(fn){
return curry.adaptTo.call(this, fn.length, fn)
}
window.curry = curry;
})();
Function.prototype.compose = function(argFunction) {
var invokingFunction = this;
return function() {
return invokingFunction.call(this,argFunction.apply(this,arguments));
}
}