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?
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.
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.