Un-bind your JS with curry

for cleaner point-free function composition

Curried functions provide many benefits. There are plenty of pieces written about what currying is and its benefits, so I won’t belabor the point here. All you really need to know to get going is that:

Currying is the process of converting functions that take multiple arguments into ones that, when supplied fewer arguments, return new functions that accept the remaining arguments.

Lets explore how code like the following:

map(fn.bind(null, id))

Can be easily refactored into more intention-revealing code like:

map(fn(id))

I do concede that the change is subtle; however, as you compose more functions this change becomes more dramatic.

I am not saying that fn.prototype.bind is bad. It is certainly one way to achieve partial application and ultimately, point-free style in JavaScript. I too am guilty of previously overusing fn.prototype.bind before I understood the benefits of curry. Ultimately, I believe that…

we can do better.

In the code below, we define and export a binaryAdd function which does exactly what you’d think it does.

// math.js
'use strict'
exports.binaryAdd = binaryAdd
/**
* Addition of two integers.
*
* @param {Number} n1
* First integer.
*
* @param {Number} n2
* Second integer.
*
* @return {Number}
* Sum of adding two integers.
*/
function binaryAdd (n1, n2) {
return n1 + n2
}

In the code below, we will import the binaryAdd function and rename it to add and use it to map over a list of integers adding 10 to the value of each integer.

'use strict'
const add = require('./math').binaryAdd
const integers = [3, 4, 5]
map(n => add(10, n), integers)
// => [ 13, 14, 15 ]

Indeed we’ve dropped a non-trivial amount of code by using arrow functions; however, shortening our code using => instead of function is not the point of this exercise and even if it was, we could still do better.

Do you recall those math sessions in elementary school where many of us learned how to reduce our equations?

In mathematics, reduction refers to the rewriting of an expression into a simpler form.
'use strict'
const add = require('./math').binaryAdd
const add10 = add.bind(null, 10)
const integers = [3, 4, 5]
map(add10, integers)
// => [ 13, 14, 15 ]

This is slightly better; though, we still have work to do here.

Consider that fn.prototype.bind takes a thisArg argument to specify the object the this keyword refers to; however, in many (albeit, most) cases, we don’t need to override this but we are forced to pass in something since thisArg is the first argument in the argument list. Generally, we hold our noses as we grudgingly pass in null or undefined.

Alternatively, if we curry binaryAdd we can drop fn.prototype.bind(null).

Let’s make a minor update to the math.js module. The binaryAdd function does not change; however, we do change our export exports.binaryAdd to be the curried form of binaryAdd.

// math.js
'use strict'
// NOTE: remember to `npm install curry2`
const curry2 = require('curry2')
exports.binaryAdd = curry2(binaryAdd)
...

We import a function (curry2) that curries binary (arity of 2) functions. We can now use the curried version of binaryAdd.

NOTE: if you are writing a non-library, you might also consider using _.curry.
'use strict'
const add = require('./math').binaryAdd
const integers = [3, 4, 5]
map(add(10), integers)
// => [ 13, 14, 15 ]

Notice that we got rid of the extra add10 variable. We no longer need it since writing add(10) is just as, if not more intention revealing than creating a variable (though, if we were to use this function multiple times, a variable would still be a good idea).

Here is what is actually happening:

  1. When we apply 10 to add, curry2 notices that our function takes two arguments. Since we’ve applied only a single argument, curry2 returns a function with arity of 1 which is used as an iterator function for map.
  2. For each integer in the list, the map function applies the 10 and the given integer (13, 14, or 15) for the current iteration to the arity of 1 function that was returned by curry2.

To round this all out, let’s look at the implementation of map. Trust me, this will make things more clear if they aren’t already:

function map (fn, list) {
var end = list.length
var idx = -1
var out = []
  while (++idx < end) {
out.push(fn(list[idx]))
}
  return out
}

I was tempted to provide a more involved refactoring which would further dramatize the use case for currying; however, we can save that exercise for a future discussion.

If you have any thoughts, suggestions, questions or feedback, just hit me up via twitter @wilmoore or github @wilmoore.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.