Javascript currying

I am trying to create a curry function that can be applied to any function and returns another, with 1 argument. Properties I want to have:

  • If the function has only one argument, the curry function should return a value: e (a); curry (f, x) = f (x);
  • If a function has many arguments, then currey should return a curry function: r (a1, a2, .., An); curry (g, x) = g2 (a2, ..., aN): g2 (a2, .. aN) = g (x, a2, ..., aN)
  • Curry function life should work as needed g.length = N => curry (g, x) .length = N-1

There are several curry implementations in the Prototype Framework and discussion on one blog . But this implementation is not very good, because it does not work very well with functions with only one argument (1), and also the returning attribute "length" of the function is 0 (3).

There is a simple implementation for the first property:

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

But I do not know how to work with the third rule, i.e. the function can be constructed as an internal 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 can no longer explicitly set the parameters. On the other hand, a function can be built using the "new Function" operator, such as:

  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 be disconnected, since an anonymous function will be created in the Global lexical environment.

So the questions are:

  • Is there a way to explicitly set the number of parameters when creating a function using the function keyword?
  • Is there a way to set up a function environment created using the 'new Function' operator?
  • Do we have a way to solve my problem in any other way?
+8
javascript lexical-closures
source share
4 answers

The way that the Functional library implements is to pass the parameters passed to "curry ()" as the first parameters to be passed. The result of the "curry" operation function then takes any additional parameters passed in the call and adds them to the end of the argument list. He doesn't care about the length of the argument list at all, because it's not a fixed thing in JavaScript at all, so it really makes no sense.

Thus:

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

Therefore, the call:

 curry(100, true); 

will look like a call:

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

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

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

These two "_" parameters mean that the received "partialFunc" should discard the first two arguments passed to it in these slots in the argument list:

 partialFunc(x + y, "Texas"); 

this way as a call:

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

I heartily recommend getting this library and looking at the code. It is surprisingly concise and clear.

One more thing: it’s important to note that since JavaScript is not a lazy evaluation language, it is actually not the same as a curry operation in a lazy functional language such as Haskell. The difference is that the arguments in “curry time” are evaluated and, therefore, sorted “as a result”. In a lazy language, everything is different.

+6
source share
 function curry(fn, args) { // no need to var these, they are scoped via argument list - we overwrite them // convert the arguments to a real array: args = [].slice.apply(arguments); // first argument is a function: fn = args.shift(); return function() { // get internal args var iArgs = [].slice.apply(arguments); // apply curried arguments, then our arguments: return fn.apply(this, args.concat(iArgs)); } } function add(a,b) { return a+b; } var add2 = curry(add, 2); alert(add2(5)); //7 var hello = curry(add, "Hello "); alert(hello("World!")); 
+3
source share

For many years, I used a function prototype for curry that looks like this:

 Function.prototype.curry = function curry() { var fn = this, args = Array.prototype.slice.call(arguments); return function curryed() { return fn.apply(this, args.concat(Array.prototype.slice.call(arguments))); }; }; 

Perhaps this will suit your needs.

You just use it like this:

 function fn1(arg1,arg2) { /*...*/ } var fn1Curried = fn1.curry('whatever'); //sets arg1 

It will work with any number of arguments.

+2
source share
 function curry(func) { var initial_args = [].slice.apply(arguments, [1]); var func_args_length = func.length; function curried(args) { if (args.length >= func_args_length) { return func.apply(null, args); } return function () { return curried(args.concat([].slice.apply(arguments))); }; } return curried(initial_args); } 
0
source share

All Articles