Monday, October 21, 2013

Partial Functions and Currying

Functions should be fun.

In fact, functions should be the reason you love javascript. Applying the knowledge of using functions as objects should be on the top of your to-do list. Of course, sometimes it benefits us to write reusable or partial functions for later use.

Take the following code example.

function partialSum(a, b){
 if(b)
   return a+b;
 return function(b){
    return a+b;
 }
}

In the case of partialSum(2), it returns a function. You don't even need to know what b is until it gets called again.

Calling partialSum(2)(3) returns 5.

This approach is limited however. How about taking it a step further?

function Partial(func, bound){
   //Convert the arguments to an array
   var args = [].slice.call(arguments, 2);
   //Return a function
   return function(){
       //function.apply takes a object and an array
    var localArgs = args.slice();
    [].push.apply(localArgs, arguments)
       return func.apply(bound, localArgs);
    //apply all arguments supplied at this 
    //point to func, call it, and return it's value
   }
}

This function returns a partial definition of the supplied function func. Seems complicated, until you realize exactly what it's doing. It's just concatenating a list of arguments and passing it to the supplied function. This approach is still limited by the number of times you can call it however.

Wouldn't it be nice to create a function that could accept any number of calls until enough parameters have been supplied to it? Ask, and ye shall receive:

//Function, Arity, BoundTo
function Curry(func, length, bound){
 //Declare initial stuff
    var args = slice.call(arguments, 3), len = length||func.length;
 //The "Magic"
    function curriedFunction(func, bound, args){
        if(len <= args.length) //then the function is ready to be called...
            return func.apply(bound,args);//pass the arguments to the function
        return function(){ //otherwise pass back a function
            var localArgs = args.slice(); //local copy...   
            push.apply(localArgs, arguments); //Concatenate arguments
            return curriedFunction(func, bound, localArgs); //and call itself
        };
    }
    return curriedFunction(func,bound,args);
}

Take a moment to really read what is going on in this function. It actually knows when to return a partial function. Function.length does a good job of determining the amount of arguments required to complete the function. This function has LOADS of practical applications.

function sum(a,b){
 return a+b;
}
var add2 = Curry(sum)(2);
console.log(add2(4));
//6
console.log(add2(6));
//8
var add10 = Curry(sum)(10);

function template(jsonObject, templateString){
 //do something here
}

var cachedJSONtemplate = Curry(template, 2, {}, JSONGetWrapper());

var result = cachedJSONtemplate("template string here...");
var result2 = cachedJSONtemplate("template2 string here...");

As you can see, the potential is great. Highly configurable code that makes reusability easy (and encouraging) is the great work of the javascript language in action!

In functional health, Josh.

No comments:

Post a Comment