Delicious ES2017 Curry

I came across this post recently. It gives the usual JavaScripty overview of currying, which is to say it does some good but also conflates a number of concepts and sacrifices functional purity for compatibility with accepted thought in the JavaScript community. I’ll let other folks address the annoying level of syncretism in posts such as that. What I’ll do is provide you with a simple curry implementation that has these simple improvements over that found in the helpful CarbonFive post:

  1. Gets rid of the semantic noise
    • Non-clarifying intermediate declarations that are only used once
    • The method call & null required when using ::call && ::apply to manipulate how functions are called
  2. Gets rid of prototype trickery (Array.prototype.slice.call.blah.blah.blah)
  3. Takes advantage of the latest JavaScript features in a way that increases declative code and decreases imperative code
  4. Gets rid of magic values such as arguments

Here it is:

1
2
3
4
5
6
7
8
9
const curry = uncurriedFunction => {
const curriedFunction = (...args) =>
args.length >= uncurriedFunction.length ?
uncurriedFunction(...args) :
(...args2) =>
curriedFunction(...([ ...args, ...args2 ]))
return curriedFunction
}

The original from CarbonFive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function curry(fx) {
var arity = fx.length;
return function f1() {
var args = Array.prototype.slice.call(arguments, 0);
if (args.length >= arity) {
return fx.apply(null, args);
}
else {
return function f2() {
var args2 = Array.prototype.slice.call(arguments, 0);
return f1.apply(null, args.concat(args2));
}
}
};
}

We now have a curry implementation that is somehow both shorter and easier to understand. Instead of meaningless declarations like f1, we’ve curriedFunction et al. We’ve traded the arcane Array.prototype.slice.call for the perhaps equally arcane ...—but at least one is a language feature that takes up less space.

A final point: this curry implementation supports passing more than one argument at a time, which isn’t kosher in the world of pure functional programming. In that world, you need to ensure each function takes one argument at a time—that’s the contract between functions that allows things like compose, pipe et al work. This isn’t a minor point, but curry here works both 1 & >1 argument at a time, so just use it as your concious guides you and you’ll be fine.

Curry away!