Curried Functions

It's a common pattern to define functions that take two or more arguments and return a single result. The function defined below, for example, takes two integers and returns their sum:

func add(_ a: Int, _ b: Int) -> Int {
	return a + b
}

If the function add were stored as a variable, what would its type be?

Answer

The type of add is (Int, Int) -> Int.

In programming languages where functions are first-class citizens, it's possible to define functions that — instead of returning a single variable — return other functions.

The curried form of the add function is defined like so:

func addCurried(_ a: Int) -> (Int) -> Int {
	return { b in a + b }
}

The type of addCurried is subtly different, (Int) -> (Int) -> Int. With parentheses for clarity, the type can also be written like so: (Int) -> ((Int) -> Int). addCurried only takes one argument, a, and returns a function that also takes one argument and returns an Int.

Naturally, the two functions produce the same result:

add(1, 2)        // = 3
addCurried(1)(2) // = 3

The key difference to remember is that add must takes both of its arguments at once, whereas addCurried is able to take arguments one a time. Notice how the parentheses are a bit different when calling these functions — (1,2) vs. (1)(2) for the curried version.

Functions that take arguments in this way — "one by one", while returning functions — are called curried functions. The name "curry" or "curried" doesn't have any practical meaning. Instead, it's named after Haskell Curry, who developed this technique.

🚩

Every function that takes two arguments can be turned into a function that takes its arguments one by one.

So why is currying a useful technique? The addCurried function certainly seems more difficult to reason about than the add function, at least at first glance.

The primary motivation is that curried functions can be partially applied to create new functions! The ability to partially apply functions will unlock the ability to compose multiple functions together into something greater — but that's a topic for a future post.

For now, we can stick with a simple example of creating a new function through partial application of the addCurried function. Since the arguments in a curried function are supplied one by one, we can create a new function called increment by passing 1 to addCurried.

let increment = addCurried(1)

Since the second argument hasn't been supplied yet, we now have a function that can add 1 to anything! The type of increment is (Int) -> Int.

increment(10) // = 11

This, to me, is magical. In some sense, currying is the first step towards higher levels of abstraction used widely in functional programming.