Go Back   CodingForums.com > :: Client side development > JavaScript programming > Post a JavaScript

Before you post, read our: Rules & Posting Guidelines

Reply
 
Thread Tools Rate Thread
Enjoy an ad free experience by logging in. Not a member yet? Register.
Old 05-01-2008, 12:04 AM   PM User | #1
Trinithis
Regular Coder

 
Join Date: Jun 2007
Location: USA
Posts: 527
Thanks: 26
Thanked 74 Times in 72 Posts
Trinithis will become famous soon enough
Heavy-duty curry function

This is a generalized curry function. It does normal currying plus a bit more.

As you would expect, the curried function supports sequential partial application. However, This 'curry' function also allows for non-sequential partial application. It also allows the currying of variable argument functions.

Curry function:
Code:
Function.freeArg = {
  toString: function() {
    return "[object FreeArg]";
  }
};

function curry(f /*, n [, a] */) {
  return f.curry(arguments[1], arguments[2]);
}

Function.prototype.curry = (function() {
  var toString = function() {
    return String(this.valueOf());
  };
  return function(/* n [, a] */) {
    var f = this;
    var a = arguments[1] || [];
    var n = typeof arguments[0] == "number"
      ? arguments[0]
      : f.length;
    return function() {
      if(!arguments.length)
        return arguments.callee;
      var p = a.slice();
      var i = -1;
      while(i = p.indexOf(Function.freeArg, i + 1), i != -1)
        p[i] = p.shift.call(arguments);
      p.push.apply(p, arguments);
      if(n < 0) {
        var g = f.curry(n, p);
        g.valueOf = function() {
          return f.apply(null, p);
        };
        g.toString = toString;
        return g;
      }
      if(p.length < n || p.indexOf(Function.freeArg) != -1)
        return f.curry(n, p);
      return f.apply(null, p);
    };
  };
})();

if(!Array.prototype.indexOf)
  Array.prototype.indexOf = function(v, i) {
    var n = this.length;
    if((i |= 0) < 0) {
      i += n;
      if(i < 0)
        i = 0;
    }
    for(; i < n; ++i)
      if(this[i] === v)
        return i;
    return -1;
  };

With non-sequential partial application, supply Function.freeArg in the argument to be temporarily bypassed. In my example, I declare _ to equal Function.freeArg to give the code a more functional feel.

With variable arg currying (mandatory if you repeatedly do partial application, but otherwise not), when you curry the function, you need to specify the number of arguments to curry as a negative number, such as -1. Normally the curry function infers the number of arguments to curry as the default function argument length. In the case that you do supply the negative value length argument, any application of the returned function always return a function that always returns a function, etc. To get the value of the function, you need to force its value either explicitly or implicitly via the toString or valueOf methods of the function.



Example code:
Code:
var _ = Function.freeArg;

-------------------------------------

// a curried add function
var add = curry(function(x, y) {
  return x + y;
});

var inc = add(1);

-------------------------------------

// a curried sub function
var sub = curry(function(x, y) {
  return x - y;
});

var oneMinus = sub(1);
var dec = sub(_, 1);

-------------------------------------

// a variable argument average function
var average = curry(function() {
  var sum = 0;
  for(var i = 0; i < arguments.length; ++i) {
    sum += arguments[i];
  }
  return sum / arguments.length;
}, -1);  // note the -1 argument!

var avg_4_7_args = average(4, 7);
var avg_4_7_0_args = avg_4_7_args(0);
var avg_1_2_3_args = average(1)(2)(3);

-------------------------------------

var alert_x_y_z_w = function(x, y, z, w) {
  alert([x, y, z, w].join('\n'));
}.curry();  // OO-style

var alert_1_y_2_w = alert_x_y_z_w(1, _, 2, _);

-------------------------------------

var x = 5;

alert(inc(x));
alert(oneMinus(x));
alert(dec(x));
alert(avg_4_7_args(1, 2, 3));
alert(avg_4_7_0_args(1,2)(3)(4,5,6));
alert(avg_1_2_3_args());
alert_1_y_2_w(3, 4);
The example code doesn't show much or explain much, but what it does demonstrate is the flexibility of how you can call the curried function. To understand what currying is, you can try:
- http://ianhenderson.org/currying_in_javascript.html
- http://www.google.com/search?hl=en&q...=Google+Search
In the short, a curried function allows you to pass one argument at a time to a function, and that will return a new function if there are arguments that you "still need to pass" into the function.

Last edited by Trinithis; 05-02-2008 at 03:51 AM..
Trinithis is offline   Reply With Quote
Old 05-04-2008, 04:06 AM   PM User | #2
fside
Regular Coder

 
Join Date: Mar 2008
Posts: 301
Thanks: 2
Thanked 30 Times in 30 Posts
fside is an unknown quantity at this point
I've seen this on the web, and wondered if it doesn't provide another example of how to use this thing. I wonder, though, is this really what Curry had in mind. As I read your links, 'currying' removes an argument, first or last, so using n-1 arguments. But this 'curry', if that's what it is, is useful because it gets the machine to do what one otherwise might have to do with multiple handlers or multi-dim objects. Here, you just send function for any object, and it works in its own 'space', almost like a 'class'. It doesn't know about other objects though to test for collision. Maybe it could?

The 'moveit' function is just an example, it could be any function at all obviously. Here, since the onclick is set, initially, onclick is fired again within moveit to keep the animation going.

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html>
<head>

<style>
</style>

</head>

<body>

<p><a id="runa" href="#">Run</a> <a id="runb" href="#">Jack</a> <a id="runc" href="#">Run</a><br>


<script>

function curry(scope, func) {
	var s = scope, f = func, n = 2;
	var args = Array.prototype.slice.call(arguments, n);

	return (function () {
		var o = s || this;
		var allArgs = args.concat(Array.prototype.slice.call(arguments, 0));
		return (f.apply(o, allArgs));
		} 
	)
}

var ary = [ document.getElementById("runa"),
              document.getElementById("runb"),
              document.getElementById("runc") ];

function moveit(that, e){
	that.style.marginLeft = that.style.marginLeft || "0px";
	if (parseInt(that.style.marginLeft) <800){
		that.style.marginLeft = parseInt(that.style.marginLeft) +10 +"px";
		setTimeout(that.onclick,200);
	}
}

for (var i in ary) {
    ary[i].onclick = curry(ary[i], function(e) { moveit(this, e) });
}

</script> 

</body>
</html>
fside is offline   Reply With Quote
Old 05-04-2008, 05:16 AM   PM User | #3
Trinithis
Regular Coder

 
Join Date: Jun 2007
Location: USA
Posts: 527
Thanks: 26
Thanked 74 Times in 72 Posts
Trinithis will become famous soon enough

What you have posted is not a curry function. It's actually partial application function. While they are closely related, they are not the same thing.

Partial application is simpler to understand. Partial application is where you supply some of the arguments to a function but not all of them. Then you get a new function that remembers those and asks for the remaining functions before execution.

In a sense, curring is a technique to enable automatic partial application. More formally, currying is a technique to turn a function into a function that accepts one and only one argument. In turn, when called, that function returns another function that accepts one and only one argument . . . and so on until the 'original' function is able to be executed.

Take for example, real number subtraction (call it f) in a math sense. f requires two inputs, both of which are real number. Then it returns another real number.

Note: R x R (same as R^2) is simply a pair of real numbers (x, y).

We say that f is a function from two real number to a real number.

That is in math notation . . .
Code:
f : R x R -> R
or more compactly . . .
Code:
f : R^2 -> R
So what this is saying is that f takes two real numbers for input and returns a real number.

In C++, this notation looks like:
Code:
real f(real, real)
In Haskell, this notation looks like:
Code:
f :: (Real, Real) -> Real
Well, when you curry f, call it f', you get:
Code:
f' : R -> (R -> R)
In C++, this might look something like:
Code:
realToReal f_prime(real)
In Haskell
Code:
f' :: Real -> (Real -> Real)
In all, curring a function creates a new function which allows you to feed an argument into it one at a time rather than all at once.

Hope this helps, though I'm not sure if I'm being too mathy.



Last edited by Trinithis; 05-04-2008 at 05:26 AM..
Trinithis is offline   Reply With Quote
Old 05-04-2008, 07:11 AM   PM User | #4
fside
Regular Coder

 
Join Date: Mar 2008
Posts: 301
Thanks: 2
Thanked 30 Times in 30 Posts
fside is an unknown quantity at this point
I appreciate the clarification. I've been reading a number of pages now on this, and I see the difference. Frankly, I'd never heard of either until your thread. Lot to learn.
fside is offline   Reply With Quote
Old 05-04-2008, 07:22 AM   PM User | #5
Trinithis
Regular Coder

 
Join Date: Jun 2007
Location: USA
Posts: 527
Thanks: 26
Thanked 74 Times in 72 Posts
Trinithis will become famous soon enough
Oh, I just looked at your moveit code. Just so you know, you are doing too much with your "curry" (should have been named "partialApply") function. While what you did does work, the scope variable is intended to be used only when applied on a method with a specific object is in mind. If you have seen a Function.prototype.bind function, it is similar to that.

What you have:
Code:
ary[i].onclick = curry(ary[i], function(e) { moveit(this, e) });
What you should have done:
Code:
ary[i].onclick = curry(null, moveit, ary[i]);
If you used a real curry function, such as mine, you could have written the code as one of the following:
Code:
ary[i].onclick = curry(moveit)(ary[i]);
ary[i].onclick = moveit.curry()(ary[i]);

// with mine you can save a little computation by doing it like:
ary[i].onclick = curry(moveit, null, [ary[i]]);
ary[i].onclick = moveit.curry(null, [ary[i]]);
I googled a site for Function.prototype.bind, and I found:
http://www.brockman.se/writing/metho...nces.html.utf8
It has a definition, and it looks promising in other functional programming aspects.

Last edited by Trinithis; 05-04-2008 at 07:31 AM.. Reason: grammar
Trinithis is offline   Reply With Quote
Old 05-15-2008, 04:18 AM   PM User | #6
RexxCrow
Regular Coder

 
RexxCrow's Avatar
 
Join Date: Jul 2006
Location: California
Posts: 275
Thanks: 6
Thanked 2 Times in 2 Posts
RexxCrow has a little shameless behaviour in the past
What exactly is a "curry function", curry means to beat, bash, and thrash, to tan leather, to clean the coat of a horse, I am not seeking what that has exactly to do with a function?
RexxCrow is offline   Reply With Quote
Old 05-15-2008, 05:24 AM   PM User | #7
jkd
Senior Coder

 
jkd's Avatar
 
Join Date: May 2002
Location: metro DC
Posts: 3,163
Thanks: 1
Thanked 18 Times in 18 Posts
jkd will become famous soon enough
This is a currying decorator I wrote a while ago: http://www.browserland.org/scripts/curry/

It relies on the function.length property to determine the number of arguments to expect, although that can be overrode manually.
__________________
jasonkarldavis.com
jkd is offline   Reply With Quote
Old 05-19-2008, 05:56 AM   PM User | #8
Trinithis
Regular Coder

 
Join Date: Jun 2007
Location: USA
Posts: 527
Thanks: 26
Thanked 74 Times in 72 Posts
Trinithis will become famous soon enough
@RexxCrow

It's named after Haskell Curry
Trinithis is offline   Reply With Quote
Old 05-23-2008, 06:14 AM   PM User | #9
fside
Regular Coder

 
Join Date: Mar 2008
Posts: 301
Thanks: 2
Thanked 30 Times in 30 Posts
fside is an unknown quantity at this point
Quote:
Originally Posted by RexxCrow View Post
What exactly is a "curry function"
There were links explaining it, above, and my own reference to a man's name.

I liked the closure it provides, especially that PAF as it was pointed out, so that 'the computer' handles each object, previous states, present states, etc. 'Set and forget'. No need for extra arrays or objects to track that for separate instances. It's done 'automatically'.
fside is offline   Reply With Quote
Reply

Bookmarks

Jump To Top of Thread


Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 08:22 PM.


Advertisement
Log in to turn off these ads.