Created
September 16, 2010 20:26
-
-
Save cowboy/583102 to your computer and use it in GitHub Desktop.
Is this partial application? Currying? Something else? Vindaloo maybe?
This file contains hidden or 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
// See my blog post: | |
// http://benalman.com/news/2010/09/partial-application-in-javascript/ | |
// In the following code sample, invoking the curried function will always | |
// return a function until all arguments are satisfied, at which point the | |
// original function is invoked, returning its result. This means that all | |
// function arguments are required, which also allows the function to be | |
// called either like foo( 1, 2, 3 ) or foo( 1 )( 2 )( 3 ). This also means | |
// that if any argument is omitted, the original function is never invoked. | |
function curry( orig_func ) { | |
var ap = Array.prototype, | |
args = arguments; | |
function fn() { | |
ap.push.apply( fn.args, arguments ); | |
return fn.args.length < orig_func.length | |
? fn | |
: orig_func.apply( this, fn.args ); | |
}; | |
return function() { | |
fn.args = ap.slice.call( args, 1 ); | |
return fn.apply( this, arguments ); | |
}; | |
}; | |
var i = 0; | |
function a( x, y, z ) { | |
console.log( ++i + ': ' + x + ' and ' + y + ' or ' + z ); | |
}; | |
a( 'x', 'y', 'z' ); // "1: x and y or z" | |
var b = curry( a ); | |
b(); // nothing logged, `a` not invoked | |
b( 'x' ); // nothing logged, `a` not invoked | |
b( 'x', 'y' ); // nothing logged, `a` not invoked | |
b( 'x' )( 'y' ); // nothing logged, `a` not invoked | |
b( 'x' )( 'y' )( 'z' ); // "2: x and y or z" | |
b( 'x', 'y', 'z' ); // "3: x and y or z" | |
var c = curry( a, 'x' ); | |
c(); // nothing logged, `a` not invoked | |
c( 'y' ); // nothing logged, `a` not invoked | |
c( 'y', 'z' ); // "4: x and y or z" | |
c( 'y' )( 'z' ); // "5: x and y or z" | |
var d = curry( c, 'y' ); | |
d(); // nothing logged, `c` not invoked | |
d( 'z' ); // "6: x and y or z" | |
var e = curry( a, 'x', 'y' ); | |
e(); // nothing logged, `a` not invoked | |
e( 'z' ); // "7: x and y or z" | |
var f = curry( a, 'x', 'y', 'z' ); | |
f(); // "8: x and y or z" | |
// Contrast that with this partial application approach (which I had | |
// incorrectly learned as “currying”). Invoking the partially applied | |
// function will always invoke the original function, and if any arguments | |
// are omitted, they are simply undefined. This allows for any number of | |
// arguments, but must be called like foo( 1, 2, 3 ) and not foo( 1 )( 2 )( 3 ). | |
function partial( orig_func ) { | |
var aps = Array.prototype.slice, | |
args = aps.call( arguments, 1 ); | |
return function() { | |
return orig_func.apply( this, args.concat( aps.call( arguments ) ) ); | |
}; | |
}; | |
var j = 0; | |
function m( x, y, z ) { | |
console.log( ++j + ': ' + x + ' and ' + y + ' or ' + z ); | |
}; | |
m( 'x', 'y', 'z' ); // "1: x and y or z" | |
var n = partial( m ); | |
n(); // "2: undefined and undefined or undefined" | |
n( 'x' ); // "3: x and undefined or undefined" | |
n( 'x', 'y' ); // "4: x and y or undefined" | |
n( 'x', 'y', 'z' ); // "5: x and y or z" | |
var o = partial( m, 'x' ); | |
o(); // "6: x and undefined or undefined" | |
o( 'y' ); // "7: x and y or undefined" | |
o( 'y', 'z' ); // "8: x and y or z" | |
var p = partial( o, 'y' ); | |
p(); // "9: x and y or undefined" | |
p( 'z' ); // "10: x and y or z" | |
var q = partial( m, 'x', 'y' ); | |
q(); // "11: x and y or undefined" | |
q( 'z' ); // "12: x and y or z" |
I wrote a blog post about this here: http://benalman.com/news/2010/09/partial-application-in-javascript/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Somewhat inspired by the "bonus" question in Rebecca Murphey's gist and Snover's JSfiddle plus a whole lot of "subconscious percolation."