Currying is this thing to make add3 functions.

“Currying” isn’t a a foreign concept, but neither has it been a particularly useful concept for me. Everytime I read about currying, it is about that function that accepts 2 numbers and returns a sum of those 2 numbers

function add(x, y) { return x + y }

add(3,2) // => returns 5

and magic happens when

// but if you curry it
add3 = add(3)

// you can use `add3` anytime, anywhere you ever need to add `3` to things!!1!
add3(2) // => returns 5

anytime? anywhere? amazeballs.

Ok ok, if not add3 as illustration, then what? Switching from not-ever-using-it[1] to using-it-multiple-times-recently[2], I’d like to offer a more relevant and practical example

Let’s say we’re trying to process a list of content… e.g. tweets

function processTweet(tweet) {
    return `{tweet.User} says, “{tweet.Text}”`
}

arrayOfResult = map(processTweet, arrayOfTweets)

map will pass each tweet from arrayOfTweets into function processTweet and returns an array of the corresponding result.

map(processTweet, [ { User: 'Alice', Text: 'Hello' }, { User: 'Bob', Text: 'World' } ])
// returns [ 'Alice says, “Hello”', 'Bob says, “World”' } ]

So far so good.

Say we want to enhance it with more context and find ourselves needing other things to do the processing, e.g. me or even a db cache — i.e. we want to append a list of common friends

return `{tweet.User} says, “{tweet.Text}”` + sharedBy(db, me, tweet)

So.. how do we get db and me in there?

Option 1

Without currying, we’ll have to settle for inline lambdas with closures.

let db = ...
let me = ...

arrayOfResult = map(function(tweet) {
    return `{tweet.User} says, “{tweet.Text}”` + sharedBy(db, me, tweet)
}, arrayOfTweets)

Oh dear. We’ve lost our standalone, reusable, test-friendly processTweet function.

Option 2

How about we overcomplicate processTweet to be a function returning a function instead?

function processTweet(db, me) {
    return function(tweet) {
        return `{tweet.User} says, “{tweet.Text}”` + sharedBy(db, me, tweet)
    }
}

The function source is a bit convoluted, but using it can be quite nice

arrayOfResult = map(processTweet(db, me), arrayOfTweets)

Option 3

With currying, there is no ceremony — processTweet remains a straightforward function that takes in the required arguments, does the processing, and returns the result.

function processTweet(db, me, tweet) {
    return `{tweet.User} says, “{tweet.Text}”` + sharedBy(db, me, tweet)
}

and we use it like it’s a function returning a function, simply by omitting some arguments

arrayOfResult = map(processTweet(db, me), arrayOfTweets)

To me, that’s pretty sweet.

UPDATE: I’ve also shared more about currying at Life Without Footguns (Part 2)

[1] I do closures with lambdas or functions returning functions, but they are different from currying
[2] Elm