-
-
Save isaacs/232700 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
// transform the function such that you can fix any of the arguments, | |
// passing _ as a "placeholder". | |
// for example: | |
// function timesSquared (x, y) { return x * y * y } | |
// var times2Squared = _(timesSquared)( _, 2 ) or timesSquared._(_, 2); | |
// times2Squared(3) = timesSquared(3, 2) = 12 | |
// In the _ified function, if all the spaces are not filled in, it returns | |
// another _ified function with the remaining spaces blank. | |
// More examples: | |
// f._(_,_) ==> curryable function requiring 2 args | |
// f._(_,_)(x) ==> f._(x,_) ==> function curried to 1 arg, requiring one more for execution | |
// f._(_)._(_) ==> passing _ as an arg does nothing, so this is the same as just f._(_) | |
// in general, doing ._() just needs to happen once. doing it a second time is unnecessary. | |
// f._(_)._(_,_) ==> passes _ as the first arg, then the second, so now it's the same as f._(_,_) | |
// f._(_,_)(_,_,_) ==> f._(_,_,_) | |
// f._(_,_,_)(1,_,2) ==> f._(1,_,2) awaiting second argument. | |
// f._(_,_,_)(1,_,2)(3) ==> f._(1,_,2)(3) ==> f(1,3,2) | |
// f._(_,1)(2,3,4) ==> f(2,1,3,4) additional args are appended to list. | |
// f._(___,_,1) ==> additional args go in the ___ member, if one is provided. | |
// f._(___,_,1)(2) ==> f._(___,2,1) right-hand curried with 2,1 | |
// f._(___,_,1)(2)(3,4,5) ==> f._(___,2,1)(3,4,5) ==> f(3,4,5,2,1) | |
// f._(___,1,2) is just like Y.rbind(f, null, 1, 2) | |
// f._(1,___,_,2)(3,4,5) ==> f._(1,___,3,2)(4,5) ==> f(1,4,5,3,2) | |
// extra args will go into the first ___ that is found. so, if there are two, you have to call it twice. | |
// f._(___,___)(1,2)(3,4,5) ==> f._(1,2,___)(3,4,5) ==> f(1,2,3,4,5) | |
// without any _ or ___ in the arguments, it'll just execute the function. | |
// furthermore | |
// _(f) === f._ | |
// _(f)(1,_) === f._(1,_) | |
// etc. | |
// But wait! There's more! | |
// f._(1,2,3) ==> ? | |
// No holes, so what happens? | |
// It doesn't quite make sense to have it just do the same thing as | |
// f(1,2,3) | |
// So, instead, it returns a callback that can take _ arguments. | |
// So: | |
// f._(1,2,3)(a,_,c)(b) === f(1,2,3,a,b,c) | |
// setTimeout(foo._(bar)),0) === setTimeout(function () { foo(bar) }, 0) | |
// f._()(1,_,2,___)(3)(4,5,6) === f._(1,3,2,___)(4,5,6) === f(1,3,2,4,5,6) | |
// f._(1,2)(___,3,4)(5,6) === f._(1,2,___,3,4)(5,6) === f(1,2,5,6,3,4) | |
// So how do I fix scope, you ask? | |
// With the ___! | |
// It works just like ___, but the first argument is used to set the scope. | |
// so, | |
// f.___(obj, 1, 2) ==> function () { return f.call(obj, 1, 2) } | |
// this doesn't execute immediately, because there's always at least one | |
// level of indirection (otherwise it's pointless, and you can always add () | |
// if you really want it to execute right away.) | |
// ___(obj, f)(1, 2) is the similiar as that | |
// And of course, this works, too: | |
// f.___(obj, 1)(_, 2)(3) ==> f.call(obj, 1, 3, 2) | |
// expose it! | |
var GLOBAL = (typeof exports !== 'undefined') ? exports : this; | |
GLOBAL._ = Function.prototype._ = _; | |
GLOBAL.___ = Function.prototype.___ = ___; | |
_.toString = ___.toString = function () { return this.name }; | |
function arr (a, i) { return Array.prototype.slice.call(a, i || 0) }; | |
function shared (u, origArgs, argExpect, me, fn, scope) { | |
var args = arr(origArgs, argExpect); | |
if (typeof(me) !== "function") { | |
if (typeof(fn) !== "function") throw new TypeError( | |
"Invalid argument(s) to "+u.name | |
); | |
return function () { | |
return u.apply(fn, args.concat(arr(arguments,0))); | |
} | |
} | |
if (me === _ || me === ___) throw new Error( | |
"While I respect your appreciation for meta, underscoring an underscore is not allowed." | |
); | |
return curry(me, arr(origArgs, argExpect - 1), scope); | |
}; | |
function _ (fn) { return shared(_, arguments, 1, this, fn) }; | |
function ___ (scope, fn) { return shared(___, arguments, 2, this, fn, scope) }; | |
function curry (fn, args, scope, call) { | |
for ( | |
var i = 0, l = args.length; i < l; i ++ | |
) if ( | |
args[i] === _ || args[i] === ___ | |
) return fixArgs(fn, args, scope); | |
if (call === _) { | |
return fn.apply(scope || this, args); | |
} | |
return function _ified () { | |
return curry(fn, args.concat(arr(arguments,0)), scope || this, _); | |
}; | |
}; | |
function fixArgs (fn, fixedArgs, scope) { return function _ified () { | |
var newArgs = []; | |
for ( | |
var f = 0, fl = fixedArgs.length, a = 0, al = arguments.length; | |
f < fl; | |
f ++ | |
) if ( | |
fixedArgs[f] === _ // fill with 1 | |
) newArgs.push(a < al ? arguments[a++] : _); | |
else if ( | |
fixedArgs[f] === ___ // fill with the rest | |
) newArgs = newArgs.concat( | |
a < al ? arr(arguments, a) | |
: ___ | |
), a = al; | |
else newArgs.push(fixedArgs[f]); | |
return curry.call(this, fn, newArgs, scope, _); | |
}}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment