The Partial Function Application in JavaScript | WebReference

The Partial Function Application in JavaScript

By Rob Gravelle


[next]

The partial function application is an effective way to apply a range of different inputs to a single object or bind one of the arguments to a function as a constant. It's like taking a snapshot of a variable or object at a point in time so we can refer to it later. The partial function application takes place when a function returns another function where some of the arguments have been pre-applied. By returning another function rather than the results of the function, the script remembers the value of parameters that were originally passed to it. This construct, called a closure, causes memory to remain allocated because the inner function holds a reference to the outer function's variables. As long as the outer variable isn't null, neither function can be garbage collected. This article covers how to use closures to perform two types of partial application called binding and currying.

Before we get into the differences between the two, let's look at a practical example of a partial function application to tally a vote count:

The tallyVotes() function takes a number, adds it to a global totalVotes variable, and returns the new total. It's simple, but the use of the global totalVotes variable could be dangerous because it can be modified by any function at any time, making the counting process unreliable. Unless you want to be doing a lot of recounts by hand, it would be wise to make totalVotes a local static variable so no other function can change it. To do that, we'll create a function called getTallyVotesFunction() that returns the real function and pre-applies the totalVotes:

The totalVotes variable will be created locally within the getTallyVotesFunction() function's scope. In fact, the entire tallyVotes() function is private to getTallyVotesFunction(), but we can get a reference to the tallyVotes() function because it's returned from the outer function. Thus, the totalVotes argument acts a seed to set the starting vote count. Setting getTallyVotesFunction to null acts as a self-destruct mechanism so the vote cannot be reset once the counting has begun (presumably the tallyVotes() function would also have some validation built in as well). The following code tests the assertions that the totalVotes will be retained in memory and that it cannot be reset using getTallyVotesFunction():

The first alert would show 23 for the vote count, and the second would display the following generic error: (See Figure 1)

Function Currying vs. Binding: What's the Difference?

The partial function application comes in two flavors: currying and binding. Both use the closure construct of a function within a function to hold variables in memory, but there are a couple of ways to tell them apart. For starters, currying functions don't necessarily return a function if not enough arguments have been provided and mightreturn the result of the function once all its arguments have been passed. In contrast, binding functions will always return a function, even if all the required arguments have been supplied. The latter comes into play for event listeners, callbacks, and setTimeout() code. Another important distinction is that currying functions diminish the number of arguments by the number of arguments passed to the outer function. Take a look at the original tallyVotes() function above, and you'll see that it requires two variables: the totalVotes and the newVotes. The curried version of the function only requires the newVotes because it keeps track of the totalVotes. Binding functions often accept additional arguments later and may or may not know how many parameters to expect. A third important distinction is that binding functions always accept an object to bind the this pointer to, whereas currying functions are limited to function parameters only.

Rather than build outer functions for every function that you want to partially apply, the easier approach is to use generic curry() and bind() functions.

A Generic curry() Function

Adding the curry() function above to the function object's prototype allows it to be applied to any function by using aFunction.curry() notation. It accepts any number arguments, which will all be pre-applied. The array's slice() function is used to convert the argument collection to a true array data type. In classic partial application style, the curry() function returns another function so the pre-applied arguments will be retained in memory. The original arguments (args) are pre-appended to any additional ones, using the array's concat() function.

Here's the code for an HTML page that demonstrates how the curry() function works. Copy and paste the code into a text editor and save it as an HMTL file. Then open it in your favorite browser. You should see two alert boxes: the first will say "I am uncurried." because uncurriedFunction() is called without any arguments. The second alert will say "I am curried!" to reflect that the msg argument has been passed, even though we didn't supply it directly to the curriedFunction(). We pre-applied it when we called the curry() function.

A Generic bind() Function

Here's an example of a generic bind() function, applied directly to an anonymous event handler. The Ajax HtmlXmlRequest req is the object being bound to its onreadystatechage() event. The searchString variable is being pre-applied as well. It's standard practice to use anonymous functions for event listeners because they never have to be called by name. Later, when the parseMessages() function is called, we can be certain that the this pointer and the searchString are the same as they were when we initially set the event handler:

The code for the bind() function is similar to curry()'s but includes the extra object parameter to set the this pointer to, meaning that the function will execute within the object's scope:


[next]