The Partial Function Application in JavaScript [con't]
|
Obtaining a Result from the Curry Function
The curry() function above always returns a function with pre-applied arguments. Sometimes it's more desirable to return the function result once all the parameters have been received. In order to do that, the currying function must know how many arguments to expect. This can be accomplished in one of two ways: You can either pass the number as an argument - something like aFunction.curryToResult(3) - or you can assign the parameter names in the function signature as you normally would - e.g.: aFunction(a, b, c).curryToResult(). In the curryToResult() function below, the line numberOfExpectedArguments = numberOfExpectedArguments || this.length; tests for the parameter. The this.length property is used by default and refers to the function's arguments length. The other difference is in the return function. Here, it checks the argument's length against the numberOfExpectedArguments. Once they've all been passed, it applies the method. Otherwise, it calls the original curry() function, passing the anonymous function using the arguments.callee property:
Using the curryToResult() function would be exactly the same as
the curry() function. Apply it to your function and call it with whatever
number of arguments you wish:
A More Elaborate Bind Function
Passing pre-applied variables to the function via the arguments
list works well in most circumstances, but in situations where a function is
called numerous times, such as in a loop, it can become inefficient.
The bindWithGetters() function sets the pre-applied variables once by creating
public getter methods within the scope of the function on which it's applied,
thus giving us the best of both worlds: accessibility and safety. Getter functions
can be parsed from the other parameters using their name. After removing the
object from the arguments collection, a for loop iterates through each
argument, looking for functions called "generateGetter." These are
then executed using the Function.call() function and the resulting getters are stored in the getter variable. To make them publicly accessible,
they are appended as a property to the bound function. Remaining arguments are
stored in the newArguments variable. Finally, a new function is returned
which calls the original function with the original arguments and any additional
ones concatenated together:
There a couple of helper functions that are called from the bindWithGetters() function. The first is the getName() function. It's meant to be a member of
the Function object, but appending it to the Object.prototype
gives us the ability to call it on all the arguments. It even works on primitive
types, such as: booleans, numbers and strings because the JavaScript
parser will promote these to their Object wrappers in order to call the function.
Hence, booleans becomes Booleans, numbers becomes Numbers
and strings becomes Strings. Validation inside the function returns
a blank string for any argument that isn't of type "function", so that the test arg.getName() == 'generateGetter'
will return false without an error being thrown. The Function.name
property is only supported in JavaScript 1.5, so the last resort is to parse
the function name, represented by the this pointer, from its signature
using a regular expression:
The second helper function, generateGetter(), is another
example of partial application. It goes three function levels deep by the time
it returns the getter, which is itself a function! Like getName() above, it's also a member of the Object.prototype. While we were really
only interested in functions there, we now legitimately want to accept all data
types. The first level of partial application happens when the this pointer
is stored for later use. In fact, we must store it or it will be replaced by
a function object by the time we use it. At this point, the function merely
returns a generic version of itself, which includes code to set the getterName.
This is the function called in the bindWithGetters() function. To return
the getter function, a new Function is created so that the getterName
can be used as the function name. An inline function is once again put to good
use so that the getter's return value is passed directly to the inner function:
To put our bindWithGetters() function to the test, we'll create
a function that displays the bound arguments, along with the two getters, apply
the bindWithGetters() function to it, and then call it. The object that the
arguments will be bound to is a basic JavaScript object that we'll create using
the new keyword. To identify it, we'll give it a name property.
Next we'll apply bindWithGetters() to a function called testBindWithGettersFunction(),
passing in our object, along with a number of other arguments. Two of these
are to generate public getter methods: one for a string, another for a boolean.
To create the getters, we call the generateGetter() function on the variable
and pass in the variable name — e.g.: 'a string'.generateGetter('test').
To call the generateGetter() function on a boolean, we have to enclose it in
parentheses. This will act like the quotes around the string to promote it to
a Boolean object so that our generateGetter() function can be applied to it.
Finally we call the bound function with a new argument, to make sure it's appended to the end of the pre-applied arguments collection:




