Curried functions in JavaScript (how to employ genericly, and invisibly)?

S

svend

Hey there,

I was thinking about some curried functions in JavaScript, and I made
this:

<script>
function add(a, b, c) {
if (arguments.length == 1) {
return function (d,e) {return a + d + e}
}
if (arguments.length == 2) {
return function (d) {return a + b + d}
}
return a + b + c;
}

t = add(1,2);
alert(t(3));

alert(add(1,2,3));
</script>

Which works, but it's ugly, to say the least. So I was wondering, if
there's a way, to create this, in a more generic way? I'm aware, that
it probably wont be SML like curried, which is totally transparent,
but is there anyway at all?

In a way, so that it works, as close to SML as possible, though I'm
willing to accept, some code will be needed?

Regards,
Svend
 
D

Douglas Crockford

I was thinking about some curried functions in JavaScript, and I made
this:

<script>
function add(a, b, c) {
if (arguments.length == 1) {
return function (d,e) {return a + d + e}
}
if (arguments.length == 2) {
return function (d) {return a + b + d}
}
return a + b + c;
}

t = add(1,2);
alert(t(3));

alert(add(1,2,3));
</script>

Which works, but it's ugly, to say the least. So I was wondering, if
there's a way, to create this, in a more generic way? I'm aware, that
it probably wont be SML like curried, which is totally transparent,
but is there anyway at all?

In a way, so that it works, as close to SML as possible, though I'm
willing to accept, some code will be needed?

You could write an array method sum() that would take an array and return its
sum. Then you can capture arguments as an array and apply sum to it to get a
value. That might be a little bit prettier.

http://www.crockford.com/javascript/little.html
 
D

Dom Leonard

Svend Tofte wrote:
thinking of something even more generic. Something, like an array, and
could return a new function, which would call the original function..

ala:

function x() {
return new function() {x(argumentsArray)}
}
Returning a function, that would call "x", with the arguments, passed
to the first call of "x", in the right order, and then apply the new
arguments, afterwards.

Function .length property and .apply method might be useful, ala

function makeCurry(func,oldArgs)
{
var length=func.length;
var oldLength = oldArgs ? oldArgs.length : 0;
function curry()
{
var i;
var myLength=arguments.length;
var newArgs;
if((oldLength==0) && (myLength >= length))
return func.apply(this,arguments);
if(myLength==0)
return curry; // no change
newArgs = new Array();
for(i=0; i < oldLength; ++i)
newArgs.push(oldArgs);
for(i=0; i < myLength; ++i)
newArgs.push(arguments);
return (newArgs.length >= length) ?
func.apply(this,newArgs) :
makeCurry(func,newArgs) ;
};
return length ? curry : func;
}

// as tested in Mozilla

function add(a,b,c)
{
return a+b+c;
}
var cAdd=makeCurry(add);
cAdd(2,4,8); //= 14
cAdd(2)(4)(8) //= 14
cAdd(2,4)(8) //= 14
cAdd(2)(4,8) //= 14
cAdd(2)()(4)()(8) //= 14

=======

When working up the code I ran into problems with the arguments array
not being an instance of javascript's Array constructor (I think), so
simply almalgamated old and new arguments in a loop.

HTH,
Dom
 
D

Douglas Crockford

When working up the code I ran into problems with the arguments array
not being an instance of javascript's Array constructor (I think), so
simply almalgamated old and new arguments in a loop.

That is really annoying, isn't it? A coding mistake in Netscape was reverse
engineered and copied into JScript, and then written into the standard.

This will produce a real array from arguments :

Array.prototype.slice.apply(arguments)

You can also give slice additional parameters for removing some initial
parameters from the array at the same time.

http://www.crockford.com/
 
F

Fox

If I understand the material correctly:

function
Curry()
{
this.accumulator = 0;

function curry(arry)
{
var f = arry[0];
var count = 1;
while(count < arry.length)
{
f(arry[count++]);
}
}

if(arguments.length > 1)
{
curry(arguments);
}

return this.accumulator;
}

// test:

function
add(n)
{
this.accumulator += n;
}

function
mult(n)
{
if(this.accumulator == 0)
this.accumulator = 1;
this.accumulator *= n;
}



alert(Curry(add, 1, 2, 3, 4, 5));
alert(Curry(mult, 1, 2, 3, 4, 5));

or you can just inline the functions:

Curry(function(i){ this.accumulator += i; }, 1,2,3,4,5);
etc...


************************************************
 
L

Lasse Reichstein Nielsen

In a way, so that it works, as close to SML as possible, though I'm
willing to accept, some code will be needed?


How about this currying function:
---
function curry(f,n) { // n is number of arguments for f
return function() {
var cnt = n;
var accu = [];
var self=this;
function accumulate(args) {
for(var i=0;i<args.length;i++) {
accu[accu.length]=args;
cnt--;
}
if (cnt<=0) {
return (f.apply(self,accu));
} else {
return function(){return accumulate(arguments);};
}
}
return accumulate(arguments);
}
}
---
And even
Function.prototype.curry = function(n){return curry(this,n);};

Then you can write

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

and

var g= f.curry(3);
g(1,2,3) + g(1)(2)(3) + g(1,2)(3)

/L 'the wonders of untyped languages!'
 
D

Dom Leonard

Douglas said:
This will produce a real array from arguments :

Array.prototype.slice.apply(arguments)
Thanks for the thought - whilst not using it exactly, put me on a
suitable track. FWIW, the makeCurry function reduced to

function makeCurry(func,oldArgs)
{ function curry()
{ var newArgs = arguments;
if(newArgs.length ==0)
return curry; // no change
if(oldArgs)
{ newArgs=new Array();
newArgs.push.apply(newArgs,oldArgs);
newArgs.push.apply(newArgs,arguments);
}
return (newArgs.length >= func.length) ?
func.apply(this,newArgs) :
makeCurry(func,newArgs);
}
return func.length ? curry : func;
}

Cheers,
Dom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top