Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript currying

I'm trying to create curry function that can be applied to any function and return another, with 1 of the arguments applied. Properties that I want to have:

  1. If function has only one argument curry function should return value: f(a); curry(f,x) = f(x);
  2. If function has many arguments currey should retrun curried function: g(a1,a2,..,aN); curry(g,x) = g2(a2,..,aN) : g2(a2,..aN)=g(x,a2,...,aN)
  3. Length propery of curried function should work "as needed" g.length = N => curry(g,x).length = N-1

There is some implementations of curry in Prototype Framework and discussion in one blog. But this implementation is not good because it doesn't work well on functions with only one argument (1), and also returning function 'length' attribute is 0 (3).

For first property there is an easy implementation:

 function curry(f,x) {
    if (f.length == 1) return f(x);
    ...
 }

But I don't know how to work with 3rd rule, i.e. function can be constucted as inner function since there will be a nested Lexical Environment and will be able to use f:

function curry(f,x) {
   return function() { ... }
}

but in this case I'll no longer will able to explicitly set parameters. On the other hand function can be constructed with 'new Function' statement, smth like that:

 function curry(f,x) {
    var args = [];
    for (var i=1; i<f.length; i++) {
       args.push('a'+i);
    }
    var sa = args.join();
    return new Function(sa,"return f(x,"+sa+")");
 }

But in this situation f and x will unbound because anonymous function will be created in Global Lexical Environment.

So the questions:

  1. is there a way to explicitly set parameters count when creating function with function keyword?
  2. is there a way to set Environment of function created with 'new Function' statement?
  3. us there a way to solve my problem in any other way?
like image 596
qnikst Avatar asked Mar 11 '11 13:03

qnikst


1 Answers

The way that the Functional library implements it is to take the parameters passed in to "curry()" as the first parameters to be passed. The function result of the "curry" operation will then take any additional parameters passed in when it is invoked and add them at the end of the argument list. It doesn't worry at all about argument list length, because that's not a fixed thing in JavaScript generally so there's really no point.

Thus:

var curry = myFunction.curry("Tuesday", x + y);

So calling:

curry(100, true);

will be just like calling:

myFunction("Tuesday", x + y, 100, true);

Functional has another function called "partial()" that allows a more controlled substitution of parameters. When you call "partial()", you pass in a dummy argument ("_") to indicate where "holes" are in the argument list:

var partialFunc = myFunction.partial("Tuesday", _, 100, true, _, "banana");

Those two "_" parameters mean that the resulting "partialFunc" should drop first two arguments passed to it into those slots in the argument list:

partialFunc(x + y, "Texas");

is thus just like calling:

myFunction("Tuesday", x + y, 100, true, "Texas", "banana");

I heartily recommend getting that library and looking at the code involved. It's surprisingly succinct and clear.

One more thing: it's important to note that because JavaScript is not a lazy-evaluation language, this isn't really the same as the "curry" operation in a lazy functional language like Haskell. The distinction is that the arguments at "curry time" are evaluated and therefore sort-of "cooked" into the result. In a lazy language, things are different.

like image 182
Pointy Avatar answered Oct 25 '22 06:10

Pointy