As intrigued and obsessed [confession] I am with this Dynamic language, my foray into Functional programming via JS has definitely wired quite a bit of neuron networks which seem to mutate (should I say grow) as days progress. This interpretation of Currying in JS is one byproduct of such mutation.
I have been using the conventional approach advocated by Crockford in his book “JavaScript: The Good Parts” (see below)
Function.prototype.crockfordCurry = function () {
"use strict";
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function () {
return that.apply(null, args.concat(slice.apply(arguments)));
};
};
until an year back when I started re-looking at Currying as a first-class Functional programming feature and Haskell’s implementation (where almost always curried functions are used to achieve multiple arguments thus having all functions have exactly one argument) of it caught my attention and led me to think why would one need to curry as below and painstakingly compute (highlighted in code below),
function add(x) {
"use strict";
return function (y) {
return x + y;
};
}
function multiply(x) {
"use strict";
return function (y) {
return x * y;
};
}
//Creating the curried functions
var adder = add.crockfordCurry();
var multiplier = multiply.crockfordCurry();
//Finding the sum of 1,2 & 3 with the curried function
console.log(adder(adder(1)(2))(3));
//Finding the product of 1,2 & 3 with the curried function
console.log(multiplier(multiplier(1)(2))(3));
especially when computational values are more than 2 and there is little or no use of the resultant partially applied functions; thus automating this and giving user a direct option to compute the results with just a curried function that can accept any number of arguments a.k.a computational values in one shot (thereby living up to the true definition excerpt from Wikipedia below):
In mathematics and computer science, 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). It was introduced by Moses Schönfinkel and later developed by Haskell Curry.
Function.prototype.haskellCurry = function () {
"use strict";
var slice = Array.prototype.slice,
that = this;
return function () {
var jsEval,
jsApply,
fn,
args = slice.apply(arguments),
result = args[0];
jsEval = function () {
args = slice.apply(arguments);
fn = that.call(null, result);
jsApply.apply(null, args);
};
jsApply = function () {
args = slice.apply(arguments);
result = fn.call(null, args[0]);
if (args.length > 1) {
jsEval.apply(null, args.slice(1));
}
};
jsEval.apply(null, args.slice(1));
return result;
};
};
And you end up using the curried functions quite conveniently this way (see highlighted):
//Creating the curried functions
var adder2 = add.haskellCurry(),
multiplier2 = multiply.haskellCurry();
//Finding the sum of 1, 2 & 3 with the curried function
console.log(adder2(1, 2, 3));
//Finding the product of 1, 2, 3, 4, 5 & 6 with the curried function
console.log(multiplier2(1, 2, 3, 4, 5, 6));
I have employed the Meta-Circular Evaluator principle here, along with Partial Application, Cumulative Recursion (with tail call optimization) and Closures to devise this currying operation in JavaScript.
And most surprising was that I found this approach resonate well with a paper (“Making a Fast Curry: Push/Enter vs Eval/Apply for higher-order languages” by none other than the great Simon Peyton Jones and Simon Marlow) published by Microsoft Research on March 17, 2004. I stumbled upon this actually quite recently which prompted me to let this piece of code thaw that was lying in cold a.k.a code storage for the past one year.
Hey! I now have a way to formally or rather [computer] scientifically explain my code.
Though the authors in their paper, drew a conclusion that the ‘Eval-Apply’ currying approach should be employed in compiled implementations, I was curious to find out how it would fare in an interpreted one. Well here’s my jslitmus benchmarking results that seem to corroborate their recommendation (Charts shows ops/second – higher is better!):
Anyone who likes to fiddle around and see if they can push the bars on the ‘Eval-Apply’ implementation are free to do that at my Github Repo.
Happy Coding!