Currying
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