Does Curry Help?

It’s been two and a half years since I wrote Why Curry Helps - a little love letter to the power of using curried functions in JavaScript. It’s easily my most-read post, bringing in hundreds of readers every month.

But as time has passed, the world has changed, and so have I. Is it still a good idea to lean on this technique to squeeze more expressivity out of your code?

I’m not so convinced.

“This isn’t Haskell

When I originally presented currying as a possible addition to our toolbox at work, my colleague William (not his real name) adamantly insisted:

This isn’t Haskell!

Equally stubbornly, I argued that we should pilfer good techniques wherever we find them, no matter how obscure the source is. It took me a while to realise how right he was.

Simple might matter more, but easy still matters

In his talk ‘Simple made easy’, Rich Hickey prises apart the notions of simplicity and ease.

“Simple”, he proposes, means that concerns aren’t tangled up together. “Easy”, that something close to your current understanding.

But perfectly simple code - code that doesn’t needlessly intertwine any concerns - gains your team little if it’s outstandingly difficult to work with. What you need is a balance; simple enough to prevent bugs and survive evolving requirements, easy enough for your team to understand it without a crash-course in new techniques.

This is the first difference between Haskell and JavaScript. In Haskell, currying is a native concept. It’s the price of entry - so every Haskell developer knows it.

In JavaScript, the concept is alien. Most developers I’ve talked to find it difficult to grasp and hard to read. While you could argue it makes code simpler, it’s not close to hand enough to benefit many teams.

Symptoms and their causes

Haskell has a type system capable of catching many bugs at compile-time. When I’m stuck, I often compile programs I know are broken - and let the compiler error guide my next steps.

JavaScript takes the opposite approach, enforcing no compile-time restrictions. The upside of this is incredible flexibility. The downside is that errors can manifest themselves a long way from their cause.

Supplying a curried function with too few arguments is an easy mistake to make, and it can often only cause an error at a much later stage in the code.

var curry = require('curry');
var add = curry(function(a, b, c){ return a + b + c });

// Oops - threeP isn't a Promise of 3, as we might expect,
// it's a Promise of a unary function.
// Code that uses threeP may eventually uncover this mistake,
// and throw an error.
var threeP = Promise.resolve(1)
  .then(add(2))

Embedded in the more complex code of most applications, it’s easy to cause yourself - or a fellow developer - to waste a handful of hours searching for the origins of a mysterious function.

Arrow functions

A couple of months ago Josh Habdas commented on the original article:

Taking [ES2015] arrow functions into consideration the boilerplate shown to access the data in the examples would be simplified markedly

He’s not wrong.

Why Curry Help’s grand finale was sleek, no doubt. It represented using Promises and some utility functions to extract a list of a users’ post titles.

fetchFromServer()
    .then(JSON.parse)
    .then(get('posts'))
    .then(map(get('title')))

In a previous post, I explored how much boilerplate you can remove with arrow functions, and applying that new language feature here replaces most of the benefit that we got from curried functions in the first place:

fetchFromServer()
    .then(JSON.parse)
    .then(data => data.posts)
    .then(posts => posts.map(p => p.title))

Was I wrong?

Am I back-peddling faster than a contestant in a moonwalk race? Yup, pretty much.

While I still believe that the benefits I focussed on in Why Curry Helps exist, the article doesn’t pay enough attention to the issues with using the technique in practice. And now ES2015 has arrived, arrow functions are a much more natural way to reduce visual noise in most contexts in JavaScript.

Nowadays, I rarely reach for currying in JavaScript.

And while I still try to push the envelope, over the last 2 and a half years, I’ve seen the value of meeting people much closer to where they are.