My last post described the BindArguments function – it returns an anonymous function that, when called, calls another function with the arguments originally specified in the call to BindArguments. Put another way, BindArguments converts a function that takes n arguments into a function that takes 0 arguments. But what if we need a function that takes 1 argument?
Imagine we have a Transform function that transforms an array of numbers in place:
function Transform(array, fn)
{
ForEach(array, function (x, n) { array[n] = fn(x); });
}
We want to use this Divide function to do the transformation.
function Divide(n1, n2)
{
return n1 / n2;
}
If we want to invert the numbers in the array:
Transform(array, function (x) { return Divide(1, x); });
Can we write a binding function like BindArguments that allows us to avoid the explicit anonymous function? BindArguments won’t work, because it returns a function that takes 0 arguments. If the returned function is called with any arguments, those arguments are ignored. We need the returned function to accept an argument and call Divide with 1 and that argument. We’ll call it BindFirst, because it accepts a function that takes two arguments and binds the first one.
Transform(array, BindFirst(Divide, 1));
In other words,
BindFirst(fn, x)(y) === fn(x, y)
Can we write such a function? Of course!
function BindFirst(fn, arg)
{
return function (x) { return fn.call(this, arg, x); };
}
We can do better and create a function that will bind any number of arguments before any number of additional arguments.
// BindFirst(fn, x1, x2, …, xn)(y1, y2, …, yn) === fn(x1, x2, …, xn, y1, y2, …, yn)
function BindFirst(fn)
{
var args = [];
for (var n = 1; n < arguments.length; n++)
args.push(arguments[n]);
return function ()
{
var myargs = [];
for (var m = 0; m < arguments.length; m++)
myargs.push(arguments[m]);
return fn.apply(this, args.concat(myargs));
};
}
Outstanding. But wait; what if we want to divide the numbers in our array by 2?
Transform(array, function (x) { return Divide(x, 2); });
What we need here is a BindSecond function.
// BindSecond(fn, x)(y) === fn(y, x)
function BindSecond(fn, arg)
{
return function (x) { return fn.call(this, x, arg); };
}Transform(array, BindSecond(Divide, 2));
Better yet, BindLast, whose implementation I leave as an exercise for the reader. (It is very similar to that of BindFirst.)
// BindLast(fn, x1, x2, …, xn)(y1, y2, …, yn) === fn(y1, y2, …, yn, x1, x2, …, xn)
Is it possible to write a truly generic Bind function that can be used in place of both BindFirst and BindLast and support even more complex binding scenarios? It is, and I hope to show it to you… in a future post.