Partial Functions in JavaScript

May 18th, 2013

JavaScript (both the language itself and libraries such as Underscore) provides many functions that use closures. A closure is a block of code that is passed as an argument to a function or returned from a function. In JavaScript you pass in that code in the form of a function.

Consider the following example for calculating the 20% tip of an array of checks.

[120, 90, 180].map(function tip(check) {
  return check * 0.2
})
> [24,18,36]

The map function accepts a closure that in the example returns a 20% tip.

Rather than using an inline tip function it makes sense to pass a reference to a defined function, so we can reuse the tip function.

function tip(check) { return check * 0.2 }
tip(100)
> 20
[120, 90, 180].map(tip)
> [24,18,36]

Probably you don’t always want to tip 20%. Sometimes you want to tip a little more, other times you may want to tip a little less. Therefore you want to pass in the percentage to tip as an argument rather than tipping 20% all the time.

function tip(percentage, check) {
  return check * percentage
}

Adding the percentage as argument is of course simple. But now we can no longer pass the tip function into map since it needs an extra argument that map ain’t going to provide. Rather than using an inline function that calls tip with 20%, let’s explore another possibility.

Partial Functions

Partial functions is a functional programming concept. A partial function takes a function and fewer arguments than normal. It returns a function that takes the remaining arguments. When called the returned function call the original function with both sets of arguments.

As JavaScript is a dynamic language that can create functions on the fly, it should be possible to implement this feature.

We need a function partial that takes a function and a variable number of arguments for its input. That’s pretty easy considering that any function in JavaScript takes a variable number of arguments. These arguments can be accessed from the arguments array.

[arguments is not an actual array but an object that supports array lookups. It lacks most array functions (like map), but we can call the functions from the array prototype using arguments as their context.]

Then partial must return a function that takes the remaining arguments and, as invoked, calls the original function with the combined argument arrays. Since partial can only access arguments in form of an array we cannot call the original function (i.e. using parentheses or call) but must apply it instead. This way the array of arguments we pass to apply is mapped to arguments of the original function.

function partial(f) {
  var args = Array.prototype.slice.call(arguments, 1)
  return function() {
    var remainingArgs = Array.prototype.slice.call(arguments)
    return f.apply(null, args.concat(remainingArgs))
  }
}

Using partial the general tip function can be passed into map as a 20%-tip function like this.

[120, 90, 180].map(partial(tip, 0.2))
> [24,18,36]

This partial function only works for pure functions and not for a function that gets needs some state of the object it is in. This is due to the function being applied without a context.

Partial functions can offer a great deal of flexibility and can prevent a lot of adapter functions. Pay attention to the order of your arguments as JavaScript binds arguments by the order they are passed in. The example with the tip function obviously wouldn’t have worked out if the percentage argument was last.