Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 9 of 9
  1. #1
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts

    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 04:51 AM.
    Trinithis

  • #2
    Regular Coder
    Join Date
    Mar 2008
    Posts
    301
    Thanks
    2
    Thanked 30 Times in 30 Posts
    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>

  • #3
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts

    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 06:26 AM.
    Trinithis

  • #4
    Regular Coder
    Join Date
    Mar 2008
    Posts
    301
    Thanks
    2
    Thanked 30 Times in 30 Posts
    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.

  • #5
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts
    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 08:31 AM. Reason: grammar
    Trinithis

  • #6
    Regular Coder RexxCrow's Avatar
    Join Date
    Jul 2006
    Location
    California
    Posts
    275
    Thanks
    6
    Thanked 2 Times in 2 Posts
    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?

  • #7
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    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.

  • #8
    Regular Coder
    Join Date
    Jun 2007
    Location
    USA
    Posts
    527
    Thanks
    26
    Thanked 74 Times in 72 Posts
    @RexxCrow

    It's named after Haskell Curry
    Trinithis

  • #9
    Regular Coder
    Join Date
    Mar 2008
    Posts
    301
    Thanks
    2
    Thanked 30 Times in 30 Posts
    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'.


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •