on
What Is the Point of Currying
Currying Explained
Currying is named after the 20th-century mathematician Haskell Curry. The basic idea is to convert a single function with many arguments into many functions with a single argument each.
Let us take a simple add
function that takes 2 arguments, x
and y
as follows.
def add(x: Int, y: Int): Int = x + y
We can call add
as you would expect:
scala> val sum = add(3, 4)
sum: Int = 7
To understand currying, remember that functional programming is all about functions. There are regular first-order functions. There are also higher-order functions. Higher-order functions are:
- functions that take other functions as arguments, or
- functions that return other functions.
In order to curry the add
function, we convert it into a higher-order function that returns another function.
def add(x: Int): Int => Int = y => x + y
Now, add
is a function that takes a single argument x
, and it returns another function that takes a single argument y
.
Currying is built into Scala natively, so the same can be written as follows.
def add(x: Int)(y: Int): Int => x + y
A curried function that has not been passed all its arguments is incomplete and is said to be a partially applied function. If add
is called with only x
, it is partially applied.
// Partially applied function
scala> add(3)
res0: Int => Int = <function>
The <function>
that is returned here still has access to the x
variable which is 3
in our example. So in essence what is returned here is y: Int => 3 + y
.
To complete our example, We can immediately pas another argument y
to this new function giving us our final result. The syntax goes like this.
scala> add(3)(4)
res0: Int = 7
What is the Point?
When I tried to understand currying, I got stuck on tutorials that explained what currying is, but not why you should use it. So I misunderstood currying as simple syntactic sugar that I would never use in real coding.
The value of this technique lies in the fact that you can create partially applied functions. These in turn can be passed to other higher-order functions such as the common map
or filter
functions.
Suppose we have a long list of numbers, to which we want to add 5. First we create a partially applied function, and then pass it to the map
function.
val myList = List(1,2,3,4,5)
val add5 = add(5)
scala> myList map add5
res1: List[Int] = List(6, 7, 8, 9, 10)