paint-brush
Partial Application for Beginnersby@bitwiselover
600 reads
600 reads

Partial Application for Beginners

by Brandon NewtonAugust 2nd, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

<em>NOTE: This was originally posted on my </em><a href="https://btnwtn.com/articles/partial-application-for-beginners" target="_blank"><em>personal website</em></a><em>.</em>

Company Mentioned

Mention Thumbnail
featured image - Partial Application for Beginners
Brandon Newton HackerNoon profile picture

NOTE: This was originally posted on my personal website.

Why Use Partial Application?

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>

Understanding Partial Application

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?

Expressions

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

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

Closures

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

Putting It Together

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.

Real-World Example

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.

Conclusion

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!