NOTE: This was originally posted on my personal website.
Partial application is a core concept for writing DRY (Don’t Repeat Yourself) and expressive code. You can use partial application to compose and create new functions, and turn code like this:
function sortFilesByName() { /* … */}
function sortFilesByType() { /* … */}
function sortFilesByFilesize() { /* … */}
function sortFilesByDate() { /* … */}
<button onClick={() => sortFilesByName()}>Name</button><button onClick={() => sortFilesByType()}>Type</button><button onClick={() => sortFilesByFilesize()}>Filesize</button><button onClick={() => sortFilesByDate()}>Date</button>
into this:
function sortFilesBy(prop) { /* … */}
<button onClick={sortFilesBy('name')}>Name</button><button onClick={sortFilesBy('type')}>Type</button><button onClick={sortFilesBy('filesize')}>Filesize</button><button onClick={sortFilesBy('date')}>Date</button>
Partial application is a computer science term that describes the process of fixing some arguments to a function, which then returns a function of smaller arity. So what does that actually mean?
In Javascript we can define functions a few ways. We can declare them:
function add(x, y) { return x + y}
console.log(add)//=> ƒ add(x, y) { return x + y }
And we can use them as values, by defining them as expressions (function expressions):
const add = function (x, y) { return x + y}
console.log(add)//=> ƒ (x, y) { return x + y }
Function expressions enable us to pass functions around like any other value. This means you can pass a function as an argument to another function (e.g. map(function () {})
), and return a function from a function:
const fn = function () { return function (x) { return x }}
fn()//=> ƒ (x) { return x }
fn()(1)//=> 1
This is a core feature of Javascript and it enables us to write incredibly clean and flexible code.
Arity is a term that describes the number of arguments that a function takes. A function which accepts zero arguments is a function of zero arity. A function that accepts one argument, is a function of one arity, two arguments has an arity of two, and so on and so forth.
We can determine the arity of a function by using [Function.length](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
.
add.length//=> 2
(function (x, y) { return x + y }).length//=> 2
(function (x) { return x }).length//=> 1
(function () {}).length//=> 0
We know that we can use a function expression to return a function from a function. We can make use of this behavior by returning functions that “remember” values from the functions they were returned from. Sounds confusing, but it is actually somewhat simple:
const add = function add() { const x = 1
return function innerFunction() { return x }}
add()//=> ƒ innerFunction() { return x }
add()()//=> 1
The inner function (innerFunction
) is passed x
from the outer function and remembers that value. This inner function is called a closure because it is accessing data from the outer add
function's environment. We can rewrite add
to return a closure which accepts another variable:
const add = function add() { const x = 1
return function innerFunction(y) { return x + y }}
add()//=> ƒ innerFunction(y) { return x + y }
add()(4)//=> 5
add()(1)//=> 2
add()(0)//=> 1
And if we move x
to be an argument, and drop the naming of innerFunction
, we get:
const add = function add(x) { return function (y) { return x + y }}
add(1)(1)//=> 1
add(0)(0)//=> 0
Partial application is the process of fixing some arguments to a function, which then returns a function of smaller arity.
Because our add
function from above returns a function, we can assign a variable to the result of calling add
with one argument:
const add5 = add(5)//=> ƒ (y) { return x + y }
add5(10)//=> 15
The function add5
calls add
, and fixes (binds) the value 5
to x
. add
returns the inner function, and replaces x
with the fixed value: ƒ (y) { return 5 + y }
.
When we do this, we have partially applied the function add
. Which has produced a function (add5
) that we can use in different ways from the original.
Now that we have a function that accepts one argument, we can pass this function around to higher-order functions (functions that take functions as arguments) like map
:
[1, 2, 3].map(add5)//=> [6, 7, 8]
// same as:[1, 2, 3].map(function (y) { return 5 + y })
While the add
and add5
examples do a great job of explaining the "what" and "how" of partial application, they fail to illustrate the "why". Let's move on to some real-world examples.
Let’s take a look at the example from the beginning of this article:
function sortFilesByName() { /* … */}
function sortFilesByType() { /* … */}
function sortFilesByFilesize() { /* … */}
function sortFilesByDate() { /* … */}
<button onClick={() => sortFilesByName()}>Name</button><button onClick={() => sortFilesByType()}>Type</button><button onClick={() => sortFilesByFilesize()}>Filesize</button><button onClick={() => sortFilesByDate()}>Date</button>
Imagine we have a request to add sorting by a new field: “Last Modified”. Instead of adding a new function sortFilesByLastModified
, we can take this as an opportunity to refactor and simplify our code.
First, let’s group these functions into a plain object whose keys will represent the property we want to sort by, and values will be functions that accept a list of files to filter:
const SortBy = { name: function (files) { // sort files return sortedFiles },
size: function (files) { // sort files return sortedFiles },
/* etc. */}
I know that I want to create a single generic function instead of many onClick
handlers. This function should accept a property to filter on and return an event handler:
function sortFilesBy(property) { return function (event) { // ??? }}
We can use this function like so:
<button onClick={sortFilesBy('name')}>Name</button><button onClick={sortFilesBy('type')}>Type</button><button onClick={sortFilesBy('filesize')}>Filesize</button><button onClick={sortFilesBy('date')}>Date</button>
When using this function we bind property
to be whatever is passed in as an argument, and return an event handler that onClick
can fire. Now we need to actually sort our files:
function sortFilesBy(property) { return function (event) { const sort = SortBy[property] //=> ƒ (files) { return sortedFiles }
this.setState(function (state) { return { files: sort(state.files) } }) }}
Because property
is fixed to whatever value we supply the sortFilesBy
function, we can use it to look up our sorting function from within the SortBy
object. This example partially applies the sortFilesBy
function to produce an event handler that sorts files based on whatever is passed to property
.
The benefit here is that instead of adding new functions to our React Component (or whatever) we can add and remove functions to the SortBy
object. This reduces the amount of lines of code from the component. Even better, it decouples the logic of filtering, and managing state from the component. SortBy
can now be reused throughout a codebase, where before it was tightly integrated into a specific component.
This is just one of the many benefits of partially applying functions. Partial application makes it easy to write composable functions, clean code, and has a whole load of other uses. I challenge you to find code in your own projects that could benefit from this technique!