Last active
August 29, 2015 14:05
-
-
Save alexpw/61619aa5924514d1180a to your computer and use it in GitHub Desktop.
Example implementation of partial function application and currying in Javascript.
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
/** | |
* A partial function applicator. | |
*/ | |
function partial() { | |
var fn = arguments[0]; | |
var slice = Array.prototype.slice; | |
var partialArgs = slice.call(arguments, 1); | |
return function () { | |
var args = slice.apply(arguments); | |
return fn.apply(null, partialArgs.concat(args)); | |
}; | |
} | |
// Test of partial | |
function foo (a, b, c) { | |
return a + b + c; | |
} | |
partial(foo, 1)(2, 3); | |
// => 6 | |
partial(foo, 1, 2)(3); // can be given any # of args | |
// => 6 | |
/** | |
* "Proper" currying (1 arg fns). | |
* | |
* "...currying is the technique of translating the evaluation of a function that | |
* takes multiple arguments (or a tuple of arguments) into evaluating a sequence | |
* of functions, each with a single argument (partial application)." - wikipedia | |
*/ | |
function curry(fn, numArgs) { | |
numArgs = numArgs || fn.length; | |
if (numArgs === 0) { | |
return fn; | |
} | |
var slice = Array.prototype.slice; | |
var currier = function (partialArgs) { | |
return function () { | |
var args = partialArgs.concat(slice.call(arguments, 0, 1)); | |
if (args.length >= numArgs) { | |
return fn.apply(null, args.slice(0, numArgs)); | |
} else { | |
return currier(args); | |
} | |
}; | |
}; | |
return currier([]); | |
} | |
var cfoo = curry(foo); | |
var cfoo1 = cfoo(1); | |
cfoo1(2)(3); | |
// => 6 | |
// Reuse foo1 and branch from it. | |
cfoo1(10)(20); | |
// => 31 | |
// Construction also accepts the # of arguments to support fns without explicit arglists. | |
curry(foo, 3)(1)(2)(3); | |
// => 6 | |
/** | |
* "Greedy" currying. Instead of strictly using single arg fns, this version | |
* accepts any number of args, up until it satisfies it's requirement (argc). | |
*/ | |
function gcurry(fn, numArgs) { | |
numArgs = numArgs || fn.length; | |
if (numArgs === 0) { | |
return fn; | |
} | |
var slice = Array.prototype.slice; | |
var currier = function (partialArgs) { | |
return function () { | |
var args = partialArgs.concat(slice.apply(arguments)); | |
if (args.length >= numArgs) { | |
return fn.apply(null, args.slice(0, numArgs)); | |
} else { | |
return currier(args); | |
} | |
}; | |
}; | |
return currier([]); | |
} | |
function foo (a, b, c, d) { | |
return a + b + c + d; | |
} | |
var gfoo = gcurry(foo); | |
var gfoo1 = gfoo(1); | |
gfoo1(2, 3)(4); // accepts more than 1 arg | |
// => 10 | |
gfoo1(10, 20, 30); // branch from gfoo1 | |
// => 61 | |
// Construction also accepts the # of arguments | |
gcurry(foo, 4)(1)(2)(3)(4); | |
// => 10 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment