Currying and partial application are two awesome techniques you may not have heard of.
This post aims to give a fairly high-level overview of both concepts in JavaScript and why you should definitely be using them.
Partial application binds any number of arguments to a function and produces another function that can be called with fewer arguments than the original.
Suppose we have a function add
that takes two arguments and returns their sum:
function add(a, b){
return a + b;
}
add(1, 2);
//=> 3
Let’s say you need to call our add
function repeatedly, but provide the same first argument every single time. You could just dive in and do this:
add(1, 2);
//=> 3
add(1, 10);
//=> 11
add(1, 258);
//=> 259
add(1, 1493022);
//=> 1493023
But you really shouldn’t. Ouch. This has the potential to become unwieldy very quickly.
Instead, we can partially apply one argument to our add
function, which gives us a new function with that argument bound to it. We can then call this new function repeatedly by supplying only the second argument.
Here we’re using Lo-Dash’s _.partial
method to perform the partial application:
var addOne = _.partial(add, 1);
addOne(2);
//=> 3
addOne(258);
//=> 259
Much better! What we’ve created using _.partial
is essentially:
function addOne(b){
return 1 + b;
}
We actually already have an ES5 method available to us that performs partial application: Function.prototype.bind
. The method takes an arbitrary list of arguments to partially apply to your function. You do additionally have to supply a context (a value for this
) as the first argument:
myFunc.bind(contextArg, arg1, arg2, ...)
It does exactly what we want it to; creates a new function with a sequence of arguments that will precede any other arguments provided when the new function is called. Perfect!
var addOne = add.bind(this, 1);
addOne(2)
//=> 3
addOne(258)
//=> 259
We can use it with functions that take more arguments too, since Function.prototype.bind
accepts an arbitary number of arguments:
function add(a, b, c, d){
return a + b + c + d;
}
var addOneAndTwoAndThree = add.bind(this, 1, 2, 3);
addOneAndTwoAndThree(4);
//=> 10
…………………………..
A curried function is one that is composed of nested unary functions (functions that take one argument) that can then be called as a chain.
The process of currying itself isn’t all that intuitive, but just to show what the transformation looks like let’s take our uncurried add
function from earlier:
function add(a, b){
return a + b;
};
In a nutshell, currying is the process of taking a function with multiple arguments and creating another function that: 1) Takes only a single argument, and 1) Returns another curried function if there are any remaining arguments
So, using these two rules, the curried version of add
would look like this:
function add(a){
return function(b){
return a + b;
}
}
Let’s break that down:
// The outer function `add` takes one argument: `a`
function add(a){
// which returns an anonymous function that takes
// a second argument: `b`
return function(b){
// This anonymous function then returns the sum
// of both arguments
return a + b;
}
}
What this means is that we can invoke our curried function using a chain of function calls
add(1)(2);
//=> 3
…or, if we supply less than the total number of required arguments, a function is returned that expects some or all of the remaining arguments:
// Supplying the first argument to `add`
var addOne = add(1);
// returns an anonymous curried function assigned to `addOne`
//=> addOne = function(b){ return a + b }
// …that’s waiting for a second argument
addOne(2);
//=> 3
This is a very contrived example, but what we’ve just done here should look familiar to you now. It’s partial application, but there’s a slight distinction to be made. We haven’t used any methods to perform the partial application for us here; the structure of the curried function is enough to accomplish it. Currying sets us up perfectly to create functions that can be very easily partially-applied. We’ll go into further detail about how to use currying in just a minute.
Before we do however, let’s be honest, no-one would sit and write all this cruft out in any language with higher-order functions and closures. Plus invoking a curried function with a chain of calls is kinda ugly to look at. To simplify things, we’re going to use curry, a simple module that aims to make creating and using curried functions way easier, allowing us to do something like this:
var curry = require('curry');
var addThree = curry(function(a, b, c){
return a + b + c;
});
addThree(1)(2)(3);
//=> 6
// Or, scrapping the ugly
addThree(1, 2, 3);
//=> 6
Swish.
So how is any of this actually useful? The real advantage of these techniques is that they give you the ability to create small, reuseable chunks of code that can then be used as building blocks.
Time for a less-contrived example.
Let’s make a little wrapper around the modulo operator and a utility function that checks whether a number is odd:
var modulo = curry(function(divisor, dividend){
return dividend % divisor;
});
modulo(3, 9);
//=> 0
// Partially applying `modulo` with the number 2 to give us our utility function
var isOdd = modulo(2);
// Which returns a truthy or falsey value
isOdd(6);
//=> 0
isOdd(5);
//=> 1
Cool. So, this is pretty useful in it’s own right, but let’s take things a bit further:
// Another wrapper, this time around the native `filter` method
var filter = curry(function(f, xs){
return xs.filter(f);
});
filter(isOdd, [1,2,3,4,5]);
//=> [1,3,5]
// Partially applying `filter` with `isOdd`
var getTheOdds = filter(isOdd);
// And we end up with a useful little function that will
// return all the odd numbers in an array
getTheOdds([1,2,3,4,5]);
//=> [1,3,5]
Now this is why these two techniques are awesome. We’ve created a pretty useful utility function that will return all the odd numbers in an array built by using a bunch of small functions as building blocks.
This method of function scaffolding is one of the fundamental techniques in functional programming.
The added benefit of using these techniques is that they encourage you to slow down; think carefully about what you’re trying to achieve and break the process down into programmatic steps. Then you can create discrete blocks of functionality for each of those steps before combining them together. You may even find a simpler way of solving old problems in the process.