chibro2 chibro2 - 4 months ago 15
Javascript Question

How do you curry any javascript function of arbitrary arity?

Let's say I have some function:

function g(a,b,c){ return a + b + c }


And I'd like to turn it into its "curried" form (in quotations since it's not exactly curried per se):

function h(a,b,c){

switch(true){

case (a !== undefined && b !== undefined && c !== undefined):
return a + b + c

case (a !== undefined && b !== undefined && c === undefined):
return function(c){ return a + b + c }

case (a !== undefined && b == undefined && c === undefined ):
return function(b,c){
return (c === undefined) ? function(c){ return a + b + c } : a + b + c
}

default:
return h

}

}


The above form has the partial binding behavior I want:

h(1) -> h(b,c)
h(1,2) -> h(c)
h(1,2,3) -> 6
h() -> h(a,b,c)


Now I'd like to automate this process into some generic function
curry
such that given any un-curried function (and maybe its number of parameters), the above function is generated. But I'm not quite sure how to implement it.

Alternatively, if the following form could be automatically created, it'd be also interesting:

function f(a,b,c){
return function(a){ return function(b){ return function(c){ return a + b + c }}}
}


Though binding
f
looks like this:

f(1)(2)(3) = 6


so it is very unwieldily and non-idiomatic, but creating the above form seem more feasible to me.

Now is could any of the above form be generated by some function, if so, how?

Answer

I believe that you could simply use Function.prototype.bind. That gives you all the flexibility you need, wheter you want the result of the function right away or simply push another value into the arguments until you decide to execute.

function sum() {
    return [].reduce.call(arguments, function (c, n) {
        return c + n;
    });
}

sum(1, 2); //3

var sum2 = sum.bind(null, 1, 2);

sum2(); //3

var sum3 = sum2.bind(null, 3);

sum3(); //6

You could also use a helper function like:

function curry(fn) {
    var c = curry.bind(this, fn = fn.bind.apply(fn, [this].concat([].slice.call(arguments, 1))));

    c.exec = fn;

    return c;
}

curry(sum, 1, 2)(3)(4, 5)(6, 7, 8).exec(); //36

Also this is very flexible as you do not have to chain, you can re-use the same curried function.

var sumOnePlus = curry(sum, 1);

sumOnePlus.exec(2); //3;
sumOnePlus.exec(3); //4;
Comments