Partial Application in JavaScript
"Cowboy" Ben Alman | January 13, 2011
Unless you've used a functional programming language such as ML or Haskell, concepts such as partial application and currying may be foreign to you. Since JavaScript supports first-class functions, once you understand these concepts, you can put them to use in your code.
Let's dive right in and take a look at a completely contrived example:
function add( a, b ) {
return a + b;
}
add( 1, 2 ); // 3
add( 1, 3 ); // 4
add( 1, 10 ); // 11
add( 1, 9000 ); // 9001
While this example is very simple, try to envision a scenario in which you must repeatedly invoke a function, passing the same first argument every single time. Since needless repetition tends to be a major cause of errors, one way to address this and DRY up that code is to store the value of the repeated argument in a variable, then reference that variable each time.
function add( a, b ) {
return a + b;
}
var one = 1;
add( one, 2 ); // 3
add( one, 3 ); // 4
add( one, 10 ); // 11
add( one, 9000 ); // 9001
In this example, using a variable as an argument substitute definitely makes this code more easily updatable, which makes it more maintainable. On the other hand, if adding one is going to be done a lot, it might be more beneficial to create a more specialized function that has this functionality built-in.
A Common Use-Case
Whether writing code just for yourself or presenting an API to your users, it's often helpful to create a more specialized function as a "wrapper" to a more generalized function if you expect to be performing a task repetitively. One way to do this would be to just define the functions manually.
// More general function.
function add( a, b ) {
return a + b;
}
// More specific functions.
function add1( b ) {
return add( 1, b );
}
function add10( b ) {
return add( 10, b );
}
add( 1, 2 ); // 3
add( 10, 3 ); // 13
add1( 2 ); // 3
add1( 3 ); // 4
add10( 2 ); // 12
add10( 3 ); // 13
While defining a few specialized functions in this manner is very simple and straightforward, if you have enough of them it can add a lot of extra code, which then has to be maintained.
A Not-Very-Flexible Solution
Because of the functional nature of JavaScript, it is possible to create a generic makeAdder
function that behaves as-follows: The makeAdder
function, when invoked with an argument, returns a new function. This returned function, when invoked with an argument, adds the new value to the initially-specified value, returning that result. The argument initially passed in to the makeAdder
function is effectively "locked in" at its value.
// More-general function.
function add( a, b ) {
return a + b;
}
// More-specific function generator.
function makeAdder( a ) {
return function( b ) {
return a + b;
};
}
add( 1, 2 ); // 3
add( 10, 3 ); // 13
var add1 = makeAdder( 1 );
add1( 2 ); // 3
add1( 3 ); // 4
var add10 = makeAdder( 10 );
add10( 2 ); // 12
add10( 3 ); // 13
Of course, while this offers the convenience of allowing you to call add1( 2 )
instead of add( 1, 2 )
, it doesn't come without a price. First, the actual adding logic is duplicated in both the add
and makeAdder
functions, which for less contrived and more complex examples can be problematic since the code is no longer as DRY as it could be. Second, for every different function you want to handle in this manner, you need to create a new makeAdder
-type function-returning function.
A Slightly More Flexible Solution
The next logical step is to create a more generalized version of the makeAdder
function that not only accepts an argument to be "locked in" but also accepts a function to be invoked. This way, you can easily reuse this functionality to create locked in versions of other functions.
// More-general functions.
function add( a, b ) {
return a + b;
}
function multiply( a, b ) {
return a * b;
}
// Relatively flexible more-specific function generator.
function lockInFirstArg( fn, a ) {
return function( b ) {
return fn( a, b );
};
}
var add1 = lockInFirstArg( add, 1 );
add1( 2 ); // 3
add1( 3 ); // 4
add1( 10 ); // 11
add1( 9000 ); // 9001
var mult10 = lockInFirstArg( multiply, 10 );
mult10( 2 ); // 20
mult10( 3 ); // 30
mult10( 10 ); // 100
mult10( 9000 ); // 90000
So what if you want to be able to "lock in" more than just that first argument? What if you have a function that accepts three arguments and you want to lock in either the first argument or both of the first two arguments, depending on the circumstance? Even though this solution is more flexible than before, it can still be improved.
Partial Application
Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.
What this means is that, given any arbitrary function, a new function can be generated that has one or more arguments "locked in," or partially applied. If you've been paying attention, you should have realized that the previous few examples have all demonstrated partial application, albeit in a not-very-generalized way.
If you're familiar with the ECMAScript 5 bind function, which not only allows a function to have its context (the this
value) overridden but some of its arguments specified as well, you've been exposed to partial application (in this example, it might help to think of this
as an implicit 0th argument that gets partially applied).
The following example is significantly more flexible than the previous examples, because it uses the arguments object to determine how many arguments need to be partially applied.
Note that the arguments object is an array-like object created when a function is invoked, accessible only inside that function, containing all of the arguments passed into that function. While arguments
is array-like, it is not an array. This means that while it has a .length
property and numeric indices, it doesn't have the normal Array.concat
or .slice
methods. In order to convert the arguments
object into an array, the native Array``.slice
method (Array.prototype.slice
) is invoked as if it existed on the arguments
object via call.
Here, the partial
function returns a function ƒ that, when invoked, invokes the originally-specified fn
function with the originally-specified arguments, followed by all arguments passed to ƒ.
function partial( fn /*, args...*/) {
var aps = Array.prototype.slice,
args = aps.call( arguments, 1 );
return function() {
return fn.apply( this, args.concat( aps.call( arguments ) ) );
};
}
// VERY BORING EXAMPLE
function add( a, b ) {
return a + b;
}
var add1 = partial( add, 1 );
add1( 2 ); // 3
add1( 3 ); // 4
add1(); // NaN
var add10 = partial( add, 10 );
add10( 2 ); // 12
add10( 3 ); // 13
add10(); // NaN
This works because the originally passed arguments
(sans the first fn
argument, which is sliced off) are stored as the args
array inside the closure that's created when the partial
function is invoked. Since the function that gets returned has access to that args
array, every time it is invoked, it invokes the originally-passed fn
function using apply. Because apply
accepts an array of arguments and concat
joins two arrays, it is easy to invoke the fn
function with the just-passed arguments
"appended" to the originally-specified args
arguments.
Note that add1()
and add10()
invoked without a numeric argument return NaN
because, while a
is a number, b
is undefined
, and adding a number to undefined
evaluates to NaN
.
Of course, partial application is most useful if you only partially apply the function arguments. If you choose to satisfy all the function arguments by specifying them all up front, you'll just end up with a function that behaves as if all its arguments had been hard-coded.
Function add( a, b ) {
return a + b;
}
var return9 = partial( add, 4, 5 );
return9(); // 9
return9( 1 ); // 9 - this is like calling add( 4, 5, 1 )
return9( 9001 ); // 9 - this is like calling add( 4, 5, 9001 )
Note that until this point, all partial
examples have only shown one specific variation of partial application, in which the leftmost function arguments are partially applied.
Partial Application: Variations
Using very similar code, it would be very easy to make a partialRight
function that applies the rightmost function arguments. In fact, all that needs to be changed is the order in which the original, partially applied, arguments are concatenated with the remaining arguments.
In this example, the partialRight
function returns a function ƒ that, when invoked, invokes the originally-specified fn
function with the arguments passed to ƒ, followed by all the originally-specified arguments.
function partialRight( fn /*, args...*/) {
var aps = Array.prototype.slice,
args = aps.call( arguments, 1 );
return function() {
return fn.apply( this, aps.call( arguments ).concat( args ) );
};
}
// SOMEWHAT GRATUITOUS EXAMPLE
function wedgie( a, b ) {
return a + ' gives ' + b + ' a wedgie.'
}
var joeGivesWedgie = partial( wedgie, 'Joe' );
joeGivesWedgie( 'Ron' ); // "Joe gives Ron a wedgie."
joeGivesWedgie( 'Bob' ); // "Joe gives Bob a wedgie."
var joeReceivesWedgie = partialRight( wedgie, 'Joe' );
joeReceivesWedgie( 'Ron' ); // "Ron gives Joe a wedgie."
joeReceivesWedgie( 'Bob' ); // "Bob gives Joe a wedgie."
While the partial
and partialRight
functions partially apply arguments to either the left or right end, there's nothing stopping us from going one step further and creating a function that allows you to cherry-pick arguments to be partially applied. The wu.js and Functional Javascript libraries both have a method called partial
that allows you to accomplish this using a placeholder value. In the following example, I'm going to name this function partialAny
.
In this example, the partialAny
function returns a function ƒ that, when invoked, invokes the originally-specified fn
function with the originally-specified arguments. However, any "placeholder" originally-specified arguments (specified with partialAny._
) will be replaced, in-order, with arguments passed when ƒ is invoked. Any remaining arguments passed to ƒ are added to the end.
Note: read up on immediately-invoked function expressions if you're unfamiliar with the (function(){ /* code */ })();
syntax.
var partialAny = (function(aps){
// This function will be returned as a result of the immediately-
// invoked function expression and assigned to the `partialAny` var.
function func( fn /*, args...*/) {
var argsOrig = aps.call( arguments, 1 );
return function() {
var args = [],
argsPartial = aps.call( arguments ),
i = 0;
// Iterate over all the originally-specified arguments. If that
// argument was the `partialAny._` placeholder, use the next just-
// passed-in argument, otherwise use the originally-specified
// argument.
for ( ; i < argsOrig.length; i++ ) {
args[i] = argsOrig[i] === func._
? argsPartial.shift()
: argsOrig[i];
}
// If any just-passed-in arguments remain, add them to the end.
return fn.apply( this, args.concat( argsPartial ) );
};
}
// This is used as the placeholder argument.
func._ = {};
return func;
})(Array.prototype.slice);
// SLIGHTLY MORE LEGITIMATE EXAMPLE
function hex( r, g, b ) {
return '#' + r + g + b;
}
var redMax = partialAny( hex, 'ff', partialAny._, partialAny._ );
redMax( '11', '22' ); // "#ff1122"
// Because `__` is easier on the eyes than `partialAny._`, let's use
// that instead. This is, of course, entirely optional, and the name
// could just as well be `foo` or `PLACEHOLDER` instead of `__`.
var __ = partialAny._;
var greenMax = partialAny( hex, __, 'ff' );
greenMax( '33', '44' ); // "#33ff44"
var blueMax = partialAny( hex, __, __, 'ff' );
blueMax( '55', '66' ); // "#5566ff"
var magentaMax = partialAny( hex, 'ff', __, 'ff' );
magentaMax( '77' ); // "#ff77ff"
While some libraries expose this partialAny
functionality as partial
, they are only able to use this name because they don't have another function called partial
. That's because they name that functionality curry
, but for the most part, that's not really the correct name for what their curry
function is doing.
Since there appears to be some confusion around currying in general, I'm going to attempt to clear things up.
Currying
Currying can be described as transforming a function of N arguments in such a way that it can be called as a chain of N functions each with a single argument.
Once a function has been curried, it is effectively "primed" for partial application, because as soon as you start passing arguments into a curried function, you are partially applying it.
The following curry
function returns a function ƒ that, when invoked, first checks to see if all of the fn
function arguments have been satisfied. If so, fn
is invoked with the originally-specified arguments, followed by all arguments passed to ƒ. If not, a function ƒ1 is returned (recursively), that behaves like function ƒ. Only once all the fn
function arguments are satisfied is fn
invoked.
Semantically, the following curry
implementation could best be described as "transforming a function of N arguments in such a way that it can be called as a chain of functions each accepting zero or more arguments, until all N arguments have been satisfied." While this technically differs from the definition of currying, it can still emulate currying, and is arguably more flexible (which is possible because JavaScript is so flexible).
Note that while functions have a .length
property that specifies the number of arguments expected by the function, in certain circumstances JavaScript cannot determine the number of expected arguments (for example, when a function uses the arguments
object instead of individual arguments). In this case, you can specify a numeric value n
before the function that will be used instead of the fn.length
property.
function curry(/* n,*/ fn /*, args...*/) {
var n,
aps = Array.prototype.slice,
orig_args = aps.call( arguments, 1 );
if ( typeof fn === 'number' ) {
n = fn;
fn = orig_args.shift();
} else {
n = fn.length;
}
return function() {
var args = orig_args.concat( aps.call( arguments ) );
return args.length < n
? curry.apply( this, [ n, fn ].concat( args ) )
: fn.apply( this, args );
};
}
// TOTALLY CONTRIVED EXAMPLE
var i = 0;
function a( x, y, z ) {
console.log( ++i + ': ' + x + ' and ' + y + ' or ' + z );
};
a( 'x', 'y', 'z' ); // "1: x and y or z"
var b = curry( a );
b(); // nothing logged, `a` not invoked
b( 'x' ); // nothing logged, `a` not invoked
b( 'x', 'y' ); // nothing logged, `a` not invoked
b( 'x' )( 'y' ); // nothing logged, `a` not invoked
b( 'x' )( 'y' )( 'z' ); // "2: x and y or z"
b( 'x', 'y', 'z' ); // "3: x and y or z"
var c = curry( a, 'x' );
c(); // nothing logged, `a` not invoked
c( 'y' ); // nothing logged, `a` not invoked
c( 'y', 'z' ); // "4: x and y or z"
c( 'y' )( 'z' ); // "5: x and y or z"
var d = curry( c, 'y' );
d(); // nothing logged, `c` not invoked
d( 'z' ); // "6: x and y or z"
var e = curry( a, 'x', 'y' );
e(); // nothing logged, `a` not invoked
e( 'z' ); // "7: x and y or z"
var f = curry( a, 'x', 'y', 'z' );
f(); // "8: x and y or z"
// THE OPTIONAL `n` ARGUMENT
function aNoLength() {
var x = arguments[0], y = arguments[1], z = arguments[2];
console.log( ++i + ': ' + x + ' and ' + y + ' or ' + z );
};
// You must specify `n` of 3 here since aNoLength.length === 0!
var g = curry( 3, aNoLength );
g(); // nothing logged, `aNoLength` not invoked
g( 'x' ); // nothing logged, `aNoLength` not invoked
g( 'x', 'y' ); // nothing logged, `aNoLength` not invoked
g( 'x' )( 'y' ); // nothing logged, `aNoLength` not invoked
g( 'x' )( 'y' )( 'z' ); // "9: x and y or z"
g( 'x', 'y', 'z' ); // "10: x and y or z"
var h = curry( 3, aNoLength, 'x' );
h(); // nothing logged, `a` not invoked
h( 'y' ); // nothing logged, `a` not invoked
h( 'y', 'z' ); // "11: x and y or z"
h( 'y' )( 'z' ); // "12: x and y or z"
Partial Application vs Currying
In terms of real-world use, what's the difference between partial application and currying? Take a look at how partially applied functions and curried functions behave in yet another contrived example:
function add( a, b, c ) {
var total = a + b + c;
return a + '+' + b + '+' + c + '=' + total;
}
add( 1, 2, 3 ); // "1+2+3=6"
// Partial application
var add1partial = partial( add, 1 );
add1partial( 2, 3 ); // "1+2+3=6"
add1partial( 2 ); // "1+2+undefined=NaN"
// Currying
var add1curry = curry( add, 1 );
add1curry( 2, 3 ); // "1+2+3=6"
add1curry( 2 ); // a function is returned (what?!)
Unlike partial application, which invokes the partially applied function whether all of its arguments have been satisfied or not, a curried function will only be invoked once all of its arguments have been satisfied, otherwise a function will be returned.
But the function that gets returned, just like any other function, can be assigned to a variable and invoked, like so:
function add( a, b, c ) {
var total = a + b + c;
return a + '+' + b + '+' + c + '=' + total;
}
var add1curry = curry( add, 1 );
add1curry( 2, 3 ); // "1+2+3=6"
add1curry( 4, 5 ); // "1+4+5=10"
var add1and2curry = add1curry( 2 );
add1and2curry( 3 ); // "1+2+3=6"
add1and2curry( 4 ); // "1+2+4=7"
Of course, just like with immediately-invoked function expressions, functions don't need to be assigned to a named variable to be invoked, all you need to do is put ()
after them. And since curried functions keep returning a function until all their arguments have been satisfied, you can write some pretty crazy looking, yet totally valid, JavaScript.
curry( add, 1 )( 2 )( 3 ); // "1+2+3=6"
curry( add, 1, 2 )( 3 ); // "1+2+3=6"
curry( add, 1, 2, 3 )(); // "1+2+3=6"
curry( add )( 1 )( 2 )( 3 ); // "1+2+3=6"
curry( add, 1 )()( 2 )()( 3 ); // "1+2+3=6"
curry( add )()( 1 )()( 2 )()( 3 ); // "1+2+3=6"
curry( add, 1 )()()()( 2 )()()()( 3 ); // "1+2+3=6"
Maybe that last example isn't exactly real-world, but doesn't it look cool?
Currying in JavaScript
In JavaScript, while currying is clearly possible, it's arguably not quite as useful as in certain "other" functional programming languages like ML and Haskell. This is mainly due to the fact that JavaScript functions behave somewhat differently than in these other languages.
In the Learn You a Haskell for Great Good!Higher order functions chapter, it is explained that, "Every function in Haskell officially only takes one parameter. So how is it possible that we defined and used several functions that take more than one parameter so far? Well, it's a clever trick! All the functions that accepted several parameters so far have been curried functions."
It turns out that in these other languages, defining a function that takes multiple arguments is really syntactic sugar for defining a series of functions each taking a single argument, as currying is built-in to the language at a very low level. This is because, in these languages, "function" doesn't really mean the same thing that it does in JavaScript. It's more like a function in mathematics, where you pass in a value and get some output, as in ƒ(x) = x + 1
.
Since every function in those other languages is curried, every function can be partially applied simply by passing less-than-all of its expected arguments. If you do that, you just get back the partially applied function.
In JavaScript, because function arguments are optional (defaulting to undefined
if omitted), you can't partially apply them without first using special functions like partial
or curry
, because a function called with less-than-all of its expected arguments behaves as if undefined
was passed for those missing arguments.
Final Words
Partial application is most commonly used to apply one or more arguments at the beginning of a function, as in the partial
examples. Its utility can most readily be seen in the ECMAScript 5 bind function, which not only allows a function to not only have its context overridden, but its arguments to be partially applied at the beginning as well.
While the other variations or partial application are also useful, they are not nearly as prevalent. That being said, the ability to "lock in" function arguments allows you to use partial application to take any function that accepts arguments and make it more specialized and easier to use.
Even though I've written some handy utility functions here, I recommend looking at the well documented and popular wu.js and Functional Javascript libraries, as well as the popular Underscore.js library, as they support most, if not all the functionality you'll typically need to leverage partial application in your JavaScript.
About the Author
Ben Alman currently works at Bocoup as Director of Training and Pluginization, where he is responsible for the development of beginner and advanced JavaScript, jQuery and HTML5 training curricula. In addition to his training and client work at Bocoup, Ben writes articles and gives presentations advocating JavaScript and jQuery code organization techniques and best practices.
Ben has created and maintains a number of very popular open source JavaScript projects and jQuery plugins and is a frequent contributor to the open source jQuery, jQuery Mobile and Modernizr projects. As an avid photographer and funk bass player, Ben loves spending time taking photos and jamming in the greater Boston area.
Find Ben on:
- Twitter - @cowboy
- Ben's Website
- GitHub