Sunday, November 22, 2009

Passing a pointer to a function with parameters in JavaScript (Partial Function Application)

In JavaScript, you can easily add a direct reference to a function with no parameters like such:

var f = function () {  };

var pf = f; // passing a direct reference to the function f

pf(); //invoking the function f

But how can you pass a reference to a function with parameters?

Say I have the following function:

var f = function (arg1, arg2) {  };

var pf = f(4, 5); // you can't do that, because that invoke f and put 
                  // it's result into pf, instead of a reference to f

pf();

One alternative is by invoking that function within an anonymous function:

var f = function (pars) {  };

var pf = function (pars) {
    f(pars);
};

pf(200);

What I am doing in the above example is wrapping the function invocation of f with an anonymous function, and passing a reference of that anonymous function to pf.

That will then allow me to invoke pf with parameters, which will in turn invoke f with the parameters passed to pf.

But in the above example, although it works, I am not passing a direct reference to function f, but rather a reference to a function that invokes f.

Fortunately enough, there is a way we can pass a direct reference to a function including its arguments, and this is called Partial Function Application.



Take a look at the following function:

var partial = function (func /*, 0..n args */) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        var allArguments = args.concat(Array.prototype.slice.call(arguments));
        return func.apply(this, allArguments);
    };
};

With the partial function, I can now do:

var f = function (pars) {  };

var pf = partial(f, 200);

pf(/* you can add more arguments */); //invokes f with the previously 
                                      //added arguments (in this case, 200);

Let's go through the partial function step-by-step:

First of all, as you can see from the function signature, I declared only one formal argument for the partial function: func; yet when I am invoking the function, I passed in more than one parameter: 

partial(f, 200);

This is because every function in JavaScript has a property named arguments (JavaScript is a functional objected-oriented language, and thus functions can have both methods and properties), which is an array-like object that allows you to access all the actual arguments in the function (I will leave further explanation of the arguments object arguments for another post).


Now in the first line of the function we are taking a copy of the arguments array, excluding the first argument (which will be the pointer to a function), func, and storing it into a variable. The reason we are doing this will be clear later on.

But how exactly am I taking a copy of this arguments object ?

To make a copy of an array in JavaScript, one should use the array's inbuilt method slice.  The slice method returns a part ( a slice) of the array it is invoked on.  But if you remember earlier on I told you that arguments is an 'array-like' object. This is because it has the length property, but lacks all the methods that are available to normal arrays; thus we cannot call slice directly from arguments (i.e. we cannot do arguments.slice

Therefore, we are calling the slice method directly from the Array.prototype, passing in arguments as the first parameter and 1 as the second parameter.  That will return a copy of the arguments 'array' excluding the first element; the first element being the function that is passed into the partial function, func.

The next line of the function is returning an anonymous function.  This means that we are declaring a closure.  The explanation of closures is beyond this post, but I will just explain it lightly.

In JavaScript, whenever a function declares an inner function and returns it, it is creating a closure.  This is because the function that is being returned (inner) will still have access to the local variables of the function that is returning it (outer), even after the outer function has returned!  In C terms, this could mean that instead of the variables in the main function being allocated in the stack-frame, they are malloc-ed!

Now back to the partial function.

The first line of the inner function is making a copy of the arguments object (of the inner function) and concatenating it with the copy of the arguments object that we copied in the outer function, args.  And that is the reason why we took a copy of the arguments object when we first entered the outer function, so now we could access it and concatenate it with the new arguments (of the inner function).

The final line is then invoking the function func that was passed as an argument (from the outer function) with the current function as it's context (this) and with an array of all the arguments we built earlier on.
This is because the apply method takes 2 arguments; the first one is the object that will be bound to this and the second an optional array arguments.