Partial Application in JavaScript
Unless you've used another functional programming language such as ML or Haskell, concepts such as partial application and currying in JavaScript may be foreign to you. That being said, once you understand these concepts, you can put them to use in your own code.
Note: this article originally appeared on the MSDN Script Junkie site, but has since been substantially rewritten. While that version of this article is more visible, this version is more current and accurate.
Functions
Even if you already understand how JavaScript functions can return functions and accept functions as arguments, I'd recommend reading this first section as a refresher. If you already know this stuff, feel free to skip ahead to the Partial Application section.
Let's dive right in and take a look at a very basic 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 you can address this and DRY up that code is to store the value-to-be-repeated in a variable, then use that variable each time you need that value.
function add(a, b) { return a + b; } var value = 1; add(value, 2); // 3 add(value, 3); // 4 add(value, 10); // 11 add(value, 9000); // 9001
As you can see, using a variable as an argument substitute definitely makes this code more easily updatable, which makes it more maintainable. On the other hand, this code is still unnecessarily repetitive. If adding the same value is going to be done many times, it might be helpful for us to create a more specialized function that has this behavior built-in.
Functions Invoking Functions
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" around a more generalized function if you expect that function to be invoked repeatedly with the same argument or arguments.
One way to accomplish this is to manually define the more specific function in addition to the more general function. This is easy in JavaScript, because functions can invoke other functions.
// More general function. function add(a, b) { return a + b; } add(1, 2); // 3 add(10, 3); // 13 // More specific functions. function addOne(b) { return add(1, b); } addOne(2); // 3 addOne(3); // 4 function addTen(b) { return add(10, b); } addTen(2); // 12 addTen(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.
Functions Returning Functions
Here, you'll see that it's possible to create a makeAdder
function that returns a new function when invoked with an argument (functions that generate other functions or objects are commonly referred to as factories). The returned function, when invoked with an argument, adds that argument's value to the originally-specified (or "bound") argument's value, returning the resulting sum.
// More general function. function add(a, b) { return a + b; } add(1, 2); // 3 add(10, 3); // 13 // More specific function generator. function makeAdder(a) { return function(b) { return a + b; }; } // More specific functions. var addOne = makeAdder(1); addOne(2); // 3 addOne(3); // 4 var addTen = makeAdder(10); addTen(2); // 12 addTen(3); // 13
This is possible because JavaScript supports closures, which allow functions to access external variables even when invoked outside their immediate scope. Additionally, in JavaScript, functions are first-class. Because of this, functions can both accept functions as arguments and return functions. Closures and first-class functions often work together, giving returned functions continued access to passed-in arguments.
While this example offers the convenience of allowing you to call addOne(2)
instead of add(1, 2)
, it doesn't come without a price. First, the actual adding logic is duplicated in both the more generic add
function and makeAdder
factory function, which can be problematic since the code is no longer as DRY as it could be. Second, for every different something
function you want to handle in this manner, you'd need to manually create a makeSomething
factory function.
Functions Accepting Functions
The next logical step is to create a more generalized factory function that not only accepts an argument to be "bound" but also accepts a function to be invoked containing all of the core logic (functions passed into other functions as arguments are commonly referred to as callbacks).
This way, a single factory function can be created that can be used to create bound (or bindable, as you'll see shortly) versions of any function.
Note that the original functions are not being modified in any way, and their behavior will not change. They are simply being referenced and invoked by the "wrapper" function.
// Relatively flexible, more specific function generator. function bindFirstArg(fn, a) { return function(b) { return fn(a, b); }; } // More general functions. function add(a, b) { return a + b; } add(1, 2); // 3 function multiply(a, b) { return a * b; } multiply(10, 2); // 20 // More specific functions. var addOne = bindFirstArg(add, 1); addOne(2); // 3 addOne(3); // 4 addOne(10); // 11 addOne(9000); // 9001 var multiplyByTen = bindFirstArg(multiply, 10); multiplyByTen(2); // 20 multiplyByTen(3); // 30 multiplyByTen(10); // 100 multiplyByTen(9000); // 90000
Where this is especially interesting is that not only can the bindFirstArg
function be used to bind any first argument to any arbitrary function, it can be used to bind a function to itself as its own first argument, thus creating a "bindable" function.
Think about it this way: If the bindFirstArg
function can bind the first argument to a function that takes two arguments--like 1
to add
or 10
to multiply
--and the bindFirstArg
function itself takes two arguments, it stands to reason that bindFirstArg
can bind a function as the first argument to itself.
Here, a more specific version of bindFirstArg
is created with its first argument, the add
function, bound to itself.
// More specific function generator. var makeAdder = bindFirstArg(bindFirstArg, add); // More specific functions. var addOne = makeAdder(1); addOne(2); // 3 addOne(3); // 4 var addTen = makeAdder(10); addTen(2); // 12 addTen(3); // 13
Does this look familiar? It should. This is the makeAdder
function, just created using a significantly more generalized approach than before.
Now, while the bindFirstArg
function is more flexible than the previous examples, it's only slightly more flexible. What if you want to be able to bind more than just that first argument? What if you have a function that accepts three or more arguments and you want to bind either the first argument or both of the first two arguments or any number of arguments, depending on the circumstance?
Even though this solution is more flexible than before, it can be generalized.
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 "bound," or partially applied. And if you've been paying attention, you've realized by now that the previous examples have demonstrated partial application in a practical, albeit somewhat limited way.
If you've ever used the ECMAScript 5 Function#bind method, which allows a function to have both its this
value and some of its arguments bound, you're already familiar with partial application. Although with Function#bind, it might help to think of the this
value as an implicit 0th argument--see the Extra Credit section at the end for a few Function#bind
examples.
Partial Application: From the Left
This example is significantly more flexible than the previous examples, because it uses the arguments object to dynamically determine the number of arguments to be bound.
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 numerically-indexed values, it doesn't have any of the Array methods, like .concat
or .slice
. In order to convert the arguments
object into an array, the native Array#slice method is invoked on arguments
using call invocation.
The following partial
function returns a function ƒ that, when invoked, invokes the fn
function with the originally-specified (bound) arguments, followed by all arguments passed to ƒ.
function partial(fn /*, args...*/) { // A reference to the Array#slice method. var slice = Array.prototype.slice; // Convert arguments object to an array, removing the first argument. var args = slice.call(arguments, 1); return function() { // Invoke the originally-specified function, passing in all originally- // specified arguments, followed by any just-specified arguments. return fn.apply(this, args.concat(slice.call(arguments, 0))); }; }
And here's an example of partial application, using the partial
function:
// Add all arguments passed in by iterating over the `arguments` object. function addAllTheThings() { var sum = 0; for (var i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; } addAllTheThings(1, 2); // 3 addAllTheThings(1, 2, 3); // 6 addAllTheThings(1, 4, 9, 16, 25); // 55 // More specific functions. var addOne = partial(addAllTheThings, 1); addOne() // 1 addOne(2); // 3 addOne(2, 3); // 6 addOne(4, 9, 16, 25); // 55 var addTen = partial(addAllTheThings, 1, 2, 3, 4); addTen(); // 10 addTen(2); // 12 addTen(2, 3); // 15 addTen(4, 9, 16, 25); // 64
This works because the originally passed arguments, minus the first fn
argument (which is sliced off), are stored as the args
array, which is created when the partial
function is invoked. Each time the returned function is invoked, it invokes the originally-passed fn
function using apply invocation. And because .apply()
accepts an array of arguments-to-be-passed and concat
joins two arrays, it is possible to invoke the fn
function with the just-passed arguments appended to the originally-specified (bound) arguments.
"Full" Application?
It's worth noting that partial application is typically most useful when only partially applying a function's 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 of its arguments had been hard-coded.
function add(a, b) { return a + b; } var alwaysNine = partial(add, 4, 5); alwaysNine(); // 9 alwaysNine(1); // 9 - this is just like calling add(4, 5, 1) alwaysNine(9001); // 9 - this is just like calling add(4, 5, 9001)
In JavaScript, if you specify more arguments than what a function expects, they will be ignored (unless they are accessed via the arguments
object). Because of this, no matter how many arguments are passed into the fully bound alwaysNine
function, the result will never change.
Partial Application: From the Right
Until this point, all examples of partial application have only shown one specific variation of partial application, in which the leftmost function arguments are bound. And while this is the most commonly seen variation of partial application, it's not the only one.
Using very similar code as with the partial
function, it's easy to make a partialRight
function that binds the rightmost function arguments. In fact, all that needs to be changed is the order in which the originally-specified arguments are concatenated with the just-specified arguments.
The following partialRight
function returns a function ƒ that, when invoked, invokes the fn
function with the arguments passed to ƒ, followed by all the originally-specified (bound) arguments.
function partialRight(fn /*, args...*/) { // A reference to the Array#slice method. var slice = Array.prototype.slice; // Convert arguments object to an array, removing the first argument. var args = slice.call(arguments, 1); return function() { // Invoke the originally-specified function, passing in all just- // specified arguments, followed by any originally-specified arguments. return fn.apply(this, slice.call(arguments, 0).concat(args)); }; }
And here's a somewhat silly example of partial application, highlighting the differences between the partial
and partialRight
functions:
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."
The only problem with this partialRight
implementation is that if too many arguments are passed into the partially applied function, the originally-specified (bound) arguments will be displaced, thus rendering them useless.
joeReceivesWedgie('Bob', 'Fred'); // "Bob gives Fred a wedgie."
While a more robust example could be written to take the function's arity (the number of arguments a function takes) into consideration, it will add additional complexity.
In JavaScript, partially applying from the left will always be simpler and more robust than partially applying from the right.
Partial Application: From Anywhere
While the partial
and partialRight
functions partially apply arguments from either the left or right, there's nothing stopping you 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 one to accomplish this using a placeholder value. In the following example, I'm going to name this function partialAny
and the placeholder value will be an arbitrary property on that function called partialAny._
.
The following partialAny
function returns a function ƒ that, when invoked, invokes the fn
function with the originally-specified (bound) arguments. However, any "placeholder" originally-specified arguments will be replaced, in-order, with arguments passed to ƒ as it is invoked. Any remaining arguments passed to ƒ will be added to the end.
Note that if you're unfamiliar with the (function(){ /* code */ }());
pattern, read my article on IIFEs aka. Immediately-Invoked Function Expressions.
var partialAny = (function() { // A reference to the Array#slice method. var slice = Array.prototype.slice; // This function will be returned as a result of the IIFE and assigned // to the external `partialAny` var. function partialAny(fn /*, args...*/) { // Convert arguments object to an array, removing the first argument. var orig = slice.call(arguments, 1); return function() { // Convert arguments object to an array. var partial = slice.call(arguments, 0); var args = []; // Iterate over the originally-specified arguments. If the argument // was the `partialAny._` placeholder, use the next just-passed-in // argument, otherwise use the originally-specified argument. for (var i = 0; i < orig.length; i++) { args[i] = orig[i] === partialAny._ ? partial.shift() : orig[i]; } // Invoke the originally-specified function, passing in interleaved // originally- and just-specified arguments, followed by any remaining // just-specified arguments. return fn.apply(this, args.concat(partial)); }; } // This is used as the placeholder argument. partialAny._ = {}; return partialAny; }());
And here's a slightly more legitimate example of partial application, using the partialAny
function.
Note that because partialAny._
is a bit verbose, a variable called __
is used instead, to make the example look nicer. The variable name could just as well be foo
or PLACEHOLDER
instead of __
.
function hex(r, g, b) { return '#' + r + g + b; } hex('11', '22', '33'); // "#112233" // A more visually-appealing placeholder. var __ = partialAny._; var redMax = partialAny(hex, 'ff', __, __); redMax('11', '22'); // "#ff1122" 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 that name because they don't already have another function called partial
. Which is because they call their partial-application-from-the-left function curry
.
This is an unfortunate--but very common--cause of confusion, because partial application and currying, while related, are two different things.
Note that the remainder of this article describes currying, which is somewhat academic and has limited practical use in JavaScript. While you're encouraged to continue reading, if your brain is on fire, you may want to skip to the article's Final Words. That being said, if you do skip the the end, you'll miss the crazy stuff.
Addendum: there's now even more crazy stuff at the very end, in the Extra Credit section. Good luck.
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.
What this means is that, once a function has been curried, it is effectively "primed" for partial application, because as soon as you pass an argument into a curried function, you are partially applying that argument. Unlike partial application, however, a curried function will keep returning curried functions until all arguments have been specified.
The following curry
function returns a function ƒ that expects one argument. When invoked, it checks to see if all of the expected fn
function arguments have been satisfied. If so, fn
is invoked with those arguments. Otherwise, another function ƒ1 is returned that behaves like function ƒ. Recursion is used to maintain an array of already-specified arguments. Once all the expected fn
function arguments are satisfied, fn
is invoked.
Note that while JavaScript functions have a .length
property that reflects that function's arity, in certain circumstances JavaScript cannot determine the number of expected arguments (for example, when a function internally uses the arguments
object instead of specifying individual arguments).
In cases where the function's arity cannot be automatically determined, you can specify a numeric argument n
that will be used instead of the fn.length
property.
function curry(fn, n) { // If `n` argument was omitted, use the function .length property. if (typeof n !== 'number') { n = fn.length; } function getCurriedFn(prev) { return function(arg) { // Concat the just-specified argument with the array of // previously-specified arguments. var args = prev.concat(arg); if (args.length < n) { // Not all arguments have been satisfied yet, so return a curried // version of the original function. return getCurriedFn(args); } else { // Otherwise, invoke the original function with the arguments and // return its value. return fn.apply(this, args); } }; } // Return a curried version of the original function. return getCurriedFn([]); }
And here's a completely contrived example of currying, using the curry
function:
var i = 0; function a(arg1, arg2, arg3) { return ++i + ': ' + arg1 + ', ' + arg2 + ', ' + arg3; } // Normal function invocation. a('x', 'y', 'z'); // "1: x, y, z" a('x', 'y'); // "2: x, y, undefined" a('x'); // "3: x, undefined, undefined" a(); // "4: undefined, undefined, undefined" // Curried function invocation. var b = curry(a); b(); // `a` not invoked, curried function returned b('x'); // `a` not invoked, curried function returned b('x')('y'); // `a` not invoked, curried function returned b('x')('y')('z'); // "5: x, y, z" b('x')('y')(); // "6: x, y, undefined" b('x')()(); // "7: x, undefined, undefined" b()('y')(); // "8: undefined, y, undefined" b()()('z'); // "9: undefined, undefined, z" b()()(); // "10: undefined, undefined, undefined" var c = b('x'); c(); // `a` not invoked, curried function returned c('y'); // `a` not invoked, curried function returned c('y')('z'); // "11: x, y, z" c('y')(); // "12: x, y, undefined" c()('z'); // "13: x, undefined, z" c()(); // "14: x, undefined, undefined" var d = c('y'); d('z'); // "15: x, y, z" d(); // "16: x, y, undefined" var e = d('z'); e; // "17: x, y, z"
Manually Specifying Function Arity
In the following example, you need to specify an n
value when currying a1
since a1.length
is 0
. This is because an arguments list wasn't specified at the time a1
was defined, and JavaScript is unable to determine how many arguments are actually being used internally.
var i = 0; function a1() { var arg1 = arguments[0]; var arg2 = arguments[1]; var arg3 = arguments[2]; return ++i + ': ' + arg1 + ', ' + arg2 + ', ' + arg3; } // Normal function invocation. a1('x', 'y', 'z'); // "1: x, y, z" a1('x', 'y'); // "2: x, y, undefined" a1('x'); // "3: x, undefined, undefined" a1(); // "4: undefined, undefined, undefined" // Curried function invocation. var b1 = curry(a1, 3); b1(); // `a` not invoked, curried function returned b1('x'); // `a` not invoked, curried function returned b1('x')('y'); // `a` not invoked, curried function returned b1('x')('y')('z'); // "5: x, y, z" b1('x')('y')(); // "6: x, y, undefined" b1('x')()(); // "7: x, undefined, undefined" b1()('y')(); // "8: undefined, y, undefined" b1()()('z'); // "9: undefined, undefined, z" b1()()(); // "10: undefined, undefined, undefined" var c1 = b1('x'); c1(); // `a` not invoked, curried function returned c1('y'); // `a` not invoked, curried function returned c1('y')('z'); // "11: x, y, z" c1('y')(); // "12: x, y, undefined" c1()('z'); // "13: x, undefined, z" c1()(); // "14: x, undefined, undefined"
If a different value for n
is specified, the curried function will simply expect that many arguments, regardless of the original function's arity.
Partial Application vs Currying
So, what's the difference between partial application and currying? Compare how partially applied functions and curried functions behave:
function add(a, b, c) { var sum = a + b + c; return a + ' + ' + b + ' + ' + c + ' = ' + sum; } add(1, 2, 3); // "1 + 2 + 3 = 6" // Partial application. var addOnePartial = partial(add, 1); addOnePartial(2, 3); // "1 + 2 + 3 = 6" addOnePartial(2); // "1 + 2 + undefined = NaN" // Currying. var addOneCurried = curry(add)(1); addOneCurried(2)(3); // "1 + 2 + 3 = 6" addOneCurried(2); // `add` not invoked, curried function returned
A partially applied function doesn't care about the number of arguments passed into it; when invoked, it will invoke the original function, merging just-passed arguments and originally-passed (bound) arguments as appropriate.
A curried function is, indeed, a chain of N functions, each one accepting a single argument; only once all N functions have been called will the original function be invoked with all specified arguments.
Partial Applicurrying: Frankenstein's Monster
Currying is conceptually difficult to understand. It's so often conflated with Partial Application that even after substantial research, I got it wrong in the original version of this article. The curry
function I actually wrote was an odd hybrid between partial application and currying. That being said, I want to show you, roughly, the abomination that I had created.
This example is almost exactly the same as the previous curry
example, but with one key difference: Instead of each curried function requiring one argument, the argument are parsed dynamically from the arguments
object (see the bold text in the next paragraph).
So, the following frankenCurry
function returns a function ƒ that expects zero or more arguments. When invoked, it checks to see if all of the expected fn
function arguments have been satisfied. If so, fn
is invoked with those arguments. Otherwise, another function ƒ1 is returned that behaves like function ƒ. Recursion is used to maintain an array of already-specified arguments. Once all the expected fn
function arguments are satisfied, fn
is invoked.
Instead of the curried function being a chain of N functions, it's a chain of as many functions as is damn well necessary, until all arguments are satisfied.
function frankenCurry(fn, n) { // If `n` argument was omitted, use the function .length property. if (typeof n !== 'number') { n = fn.length; } function getFrankenCurriedFn(prev) { return function() { // Concat all just-specified arguments with the array of // previously-specified arguments. Madness! var args = prev.concat(Array.prototype.slice.call(arguments, 0)); if (args.length < n) { // Not all arguments have been satisfied yet, so return a // franken-curried version of the original function. return getFrankenCurriedFn(args); } else { // Otherwise, invoke the original function with the arguments and // return its value. return fn.apply(this, args); } }; } // Return a franken-curried version of the original function. return getFrankenCurriedFn([]); }
While the frankenCurry
function behaves exactly the same way as curry
if you always pass just one argument in to each franken-curried function, it behaves very differently if you don't pass just one argument into any franken-curried function.
var i = 0; function a(arg1, arg2, arg3) { return ++i + ': ' + arg1 + ', ' + arg2 + ', ' + arg3; } // Curried function invocation. var b = curry(a); b(); // `a` not invoked, curried function returned b('x'); // `a` not invoked, curried function returned b('x')('y'); // `a` not invoked, curried function returned b('x')('y')('z'); // "1: x, y, z" b('x')('y')(); // "2: x, y, undefined" b('x')()(); // "3: x, undefined, undefined" b()('y')(); // "4: undefined, y, undefined" b()()('z'); // "5: undefined, undefined, z" b()()(); // "6: undefined, undefined, undefined" // Franken-curried function invocation. var c = frankenCurry(a); c(); // `a` not invoked, curried function returned c('x'); // `a` not invoked, curried function returned c('x')('y'); // `a` not invoked, curried function returned c('x')('y')('z'); // "7: x, y, z" c('x')('y')(); // `a` not invoked, curried function returned c('x')()(); // `a` not invoked, curried function returned c()('y')(); // `a` not invoked, curried function returned c()()('z'); // `a` not invoked, curried function returned c()()(); // `a` not invoked, curried function returned c('x')('y', 'z'); // "8: x, y, z" c('x', 'y')('z'); // "9: x, y, z" c('x', 'y', 'z'); // "10: x, y, z"
If you read the IIFE article, you'll know that function expressions don't need to be assigned to a named variable to be invoked; all you need to do is put parens after them. And since franken-curried functions (Franken-Curried Function Expressions? FCFEs, anyone?!) keep returning a function until all their arguments have been satisfied, you can write some pretty crazy looking, yet totally valid, JavaScript.
function add(a, b, c) { var sum = a + b + c; return a + ' + ' + b + ' + ' + c + ' = ' + sum; } frankenCurry(add)(1)(2)(3); // "1 + 2 + 3 = 6" frankenCurry(add)(1, 2)(3); // "1 + 2 + 3 = 6" frankenCurry(add)(1, 2, 3); // "1 + 2 + 3 = 6" frankenCurry(add)(1)(2, 3); // "1 + 2 + 3 = 6" frankenCurry(add)(1)()(2)()(3); // "1 + 2 + 3 = 6" frankenCurry(add)()(1)()()(2)()()()(3); // "1 + 2 + 3 = 6" frankenCurry(add)()()()()()(1)()()()()()(2)()()()()()(3); // "1 + 2 + 3 = 6"
I'll be the first to admit that franken-currying has limited real-world appeal, but then again, in JavaScript, so does currying. Besides, I'm not going for real-world appeal with this particular example. I'm going for crazy-looking JavaScript.
Currying: Practical Uses in JavaScript
Are there practical uses for currying in JavaScript? Not really.
In JavaScript, while currying is clearly possible, it's not nearly 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; currying in JavaScript is really just simulating currying in JavaScript.
The e-book Learn You a Haskell for Great Good! covers this distinction in its Higher order functions chapter. The author explains 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 using special utility functions like partial
or curry
. That's because a function called with less-than-all of its expected arguments behaves as if undefined
was passed for the 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 Function#bind function, which not only allows a function to have its context overridden, but allows its arguments to be partially applied from the left as well.
While the other variations or partial application are also useful, they are not nearly as prevalent. That being said, the ability to bind function arguments allows you to use partial application to take any function that accepts arguments and make it more specialized and possibly easier to use.
Even though I've written some handy (and not-so-handy) utility functions here, I recommend looking at the well documented and popular wu.js and Functional Javascript libraries, as well as the popular Lo-Dash and Underscore.js libraries, as they support most, if not all, the functionality you'll typically need to leverage partial application in your JavaScript.
Extra Credit
As mentioned earlier in this article, the ECMAScript 5 Function#bind method does partial application, allowing a function to have its this
value and, optionally, some number of arguments bound.
Here's a basic example illustrating how the this
value inside of a function can change depending on how a function is invoked, and how the .bind
method can be used to permanently lock in a this
value and optionally partially apply arguments.
var prop = 9001; var obj = { prop: 1, add: function(a, b) { var sum = this.prop + a + b; return this.prop + ' + ' + a + ' + ' + b + ' = ' + sum; } }; // When invoked this way, `this` inside `add` is `obj` obj.add(2, 3); // "1 + 2 + 3 = 6" // When invoked like this, `this` inside `badAdd` is the global object. var badAdd = obj.add; badAdd(2, 3); // "9001 + 2 + 3 = 9006" // The `this` value can be explicitly set using call and apply invocation. badAdd.call(obj, 4, 5); // "1 + 4 + 5 = 10" badAdd.apply(obj, [6, 7]); // "1 + 6 + 7 = 14" // When a `this` value is bound, it stays bound. var goodAdd = obj.add.bind(obj); goodAdd(8, 9); // "1 + 8 + 9 = 18" // The same goes for partially applied arguments, too. var goodAddEvenMore = obj.add.bind(obj, 10); goodAddEvenMore(11); // "1 + 10 + 11 = 22" // Note that only a reference to the `this` object itself gets locked in, // its properties can still be changed. obj.prop = 99; goodAdd(5, 6); // "99 + 5 + 6 = 110" goodAddEvenMore(7); // "99 + 10 + 7 = 116"
And now that the basics have been covered…
Extra, Extra Credit
In the Functions Accepting Functions section, the bindFirstArg
function was passed to itself to create a "bindable" function. Dave Herman took this one step farther in these three tweets, showing everyone how easy it is to create standalone bind()
, call()
and apply()
functions that accept a function as their first argument instead of having to be called as a method of that function.
var bind = Function.prototype.call.bind(Function.prototype.bind); var call = bind(Function.prototype.call, Function.prototype.call); var apply = bind(Function.prototype.call, Function.prototype.apply);
Now, I didn't come up with those three totally amazing lines of JavaScript, but I did manage to create a golfed-down version (extra, extra, extra credit) that's functionally equivalent.
While these three functions are bit mental, it illustrates a very interesting point: you don't necessarily have to invoke a function's .call
or .apply
method to set its this
value explicitly.
What on earth does this mean? Take a look at the following example, where two very different-looking statements effectively do the same thing:
fn.call(thisValue); Function.prototype.call.call(fn, thisValue);
Each statement invokes fn
with a this
value of thisValue
. The first does it by invoking the .call
method of fn
, where JavaScript sets this
inside .call
to fn
implicitly. The second does it by invoking the Function.prototype.call
method using call invocation, passing in its this
value, fn
, as the first argument, explicitly. Of course, the first argument normally passed to .call
--in this case, thisValue
--is the this
value of the function that was the this
value of .call
, but that's not really the most interesting part.
This is the most interesting part: if you can set the this
value of some function using Function#call
, you can bind the this
value of that function using Function#bind
. When using the .bind
method of .call
, you can create a function that naturally accepts a this
value as its first argument.
var callBoundFn = Function.prototype.call.bind(fn); callBoundFn(thisValue);
So, to make a long story just a little bit longer…
// Instead of having to use slice.call every time, var slice = Array.prototype.slice; var args = slice.call(arguments); // You can just use slice without .call by binding slice TO call! var slice = Function.prototype.call.bind(Array.prototype.slice); var args = slice(arguments); // Although I'd probably call it something like toArray. var toArray = Function.prototype.call.bind(Array.prototype.slice); var args = toArray(arguments);
And if you want to bind a function to .apply
, you can do that too…
// Math.max and Math.min take any number of arguments, Math.max(15, -10, 0, 5, 10); // 15 // But they don't know how to handle arrays. Math.max([15, -10, 0, 5, 10]); // NaN // So, let's fix that :) var maxArray = Function.prototype.apply.bind(Math.max, null); maxArray([15, -10, 0, 5, 10]); // 15
Cool, right?
Bonus WTF
I've been told that I don't know when to quit. A recent conversation reminded me of a few related gists, and so I dug up this one which seemed strangely relevant. Well, strange, at least.
// OOP (yes, .blink exists) 'OHAI'.blink() // '' // Call invocation. String.prototype.blink.call('OHAI') // same // $ always makes code look awesome. Right, jQuery? var $ = Function.prototype.call; // Very explicit call invocation. $.call(String.prototype.blink, 'OHAI') // same, etc... // Very, very explicit call invocation, aka... call invo-cursion? $.call($,$,$,$,$,$,$,$,$,$,$,$, String.prototype.blink, 'OHAI') // ^^^^^^^^^^^^^^^^^^^^^^^ "bonus" calls, cash money dollas // You can have fun with apply invocation and _ too! var _ = Function.prototype.apply; // Very, very explicit apply invocation, aka... apply invo-cursion? _.apply(_,[_,[_,[_,[_,[_,[_,[_, [ String.prototype.blink, ['OHAI'] ]]]]]]]]) // ^^^^^^^^^^^^^^^^^^^^^^ "bonus" applies, and fun w/brackets ^^^^^^^^
And now that I've shown you that… please, for the love of all that is good in the world, don't ever use any of it. Ok, liability crisis averted!
I'd like to thank both Mike Pennisi and Rebecca Murphey for reviewing this article and providing feedback and suggestions.
a
,b
,i
,br
,p
,strong
,em
,pre
,code
.<pre class="brush:js"></pre>
(supported syntax highlighting brushes:
js
,css
,php
,plain
,bash
,ruby
,html
,xml
)<
instead of<
and>
instead of>
in the examples themselves.