Thursday, April 15, 2010

Spicy curry for my javascript

I had came across currying when studying functional languages, and I struggled a bit with the concept. I always saw mathematical examples and complex explanations, but never a concrete example of how and why I should use it. Until the other day when I had an Aha moment.

So what is currying? It is a partial application of a function. For example you have a function that takes 2 arguments and you only pass one and wait until later to supply the second argument. This could be on a callback or in a closure.

Its also useful the other way, if you have a function that only takes 1 argument and you want to pass another argument. This is where I had my moment of zen. I was trying to get an event into a DWR callback function. The problem is the function only takes 1 argument supplied by the DWR call.

Since we are using Prototype, I discovered Prototype had a curry function also. By the way here is a good reference book on Prototype. I am starting to find the more I learn about Prototype the more I like it.

Well, enough talking, time for some code. For simplicity, I included the prototype.js in the same directory has my html.
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
function baseMethod(msg){
    alert(msg);
}

baseMethod("Normal operation");

function wrappedMsg(times,msg){
    for(i=1;i<=times;i++){
        baseMethod(msg);
    }
};
var newBaseMsg=wrappedMsg.curry(2);
newBaseMsg("Curried Operation");
</script>
In the code we have a function that takes only one argument, like the DWR callback function. By wrapping the code I am able to change the behavior of the function and the use the newBaseMsg wherever I would use the DWR callback function.


If you aren't using Prototype don't despair. I found a good link that
explains it very well.
Replacing the prototype function with the curry function will accomplish the same thing.
<script type="text/javascript">
Function.prototype.curry = function() 
{ 
  var method = this, args = Array.prototype.slice.call(arguments); 
  return function() 
  { 
    return method.apply(this, args.concat(Array.prototype.slice.call(arguments))); 
  }; 
}; 
function baseMethod(msg){
    alert(msg);
}

baseMethod("Normal operation");

function wrappedMsg(times,msg){
    for(i=1;i<=times;i++){
        baseMethod(msg);
    }
};
var newBaseMsg=wrappedMsg.curry(2);
newBaseMsg("Curried Operation");
</script>

I think we forget sometimes that JavaScript has many attributes of a functional language that make it very powerful and can provide some elegant solutions to some problems we have.