Scala functions and currying

Posted on March 28, 2015

What is currying? From Wikipedia currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions. Let’s not dive into lambda calculus, but look at it from a developer’s perspective. How does it help in real live?

Haskell

let f1 = \x y z -> x + y + z 
-- f1 :: Integer -> Integer -> Integer -> Integer

let f2 = \x -> \y -> \z -> x + y + z 
-- f2 :: Integer -> Integer -> Integer -> Integer

As we can see from types these two definitions are equivalent. All arguments are treated equally and distinguished only by their position. Given proper order it allows to create elegant transformations:

let add_2 = map (2 +)
-- add_2 :: [Integer] -> [Integer]

add_2 [1,2,3]
-- [3,4,5]

We used currying twice here: (2 +) creates a single argument function which adds 2 to it’s argument, then map (2 +) creates a single argument function which takes a list and applies previously defined function to every element of it. Nice and clean.

Scala

To start with, Scala doesn’t have Haskell-type functions. In fact, every functional object in Scala is a method. What’s the difference? Scala is OO language, so it works with objects. Objects consist of a state and methods, so every method is attached to some object. What does it change? Well, every method has an additional implicit argument, called this. So when we write 2 + 3 we in fact call some polymorphic function @+(2, 3), which in OO notation looks like 2.+(3). As a result not all arguments are created equal and currying becomes much less elegant. Let’s write (2 +) in Scala:

val plus_2: Int => Int = 2.`+`

Looks almost like Haskell, right? Wrong! What is happening here is called eta-expansion, which converts method to a function. That’s the reason why we need to specify type here. And by the way, function in Scala is an object which defines apply(). So compiler effectively created a transformation of one object into another, which is roughly equivalent to:

val plus_2: Int => Int =
  new Function1[Int, Int] {
    def apply(x: Int) = 2 + x
  } 

But similarities with Haskell stop here. Next step is to write map plus_2 and we are stuck. Since any method is attached to an object, map belongs to something. It can’t belong to plus_2 since plus_2 is just a generic <function1>. So it belongs to an instance of e.g. List (i.e. the first argument is always a List). But I’m not interested in creating generic transformation for a specific instance of a List. I can always define a partial function, but the simplicity is lost.

To curry a method with more than one parameter we can use curried, but curried only applies to a <functionN> and eta-expansion is not applicable in this context, so we have to use _:

(List(1,2,3).indexOf _).curried
// Any => (Int => Int) = <function1>

What is interesting in the above example is Any, it’s presumably there because there are two versions of indexOf, with one and two arguments, but curried in fact uses the second one because the first parameter has the same type in both and it’s not possible to distinguish between them by analyzing the first argument.

Okay, that hasn’t worked well so far. What about multiple parameter lists? They are what is usually called currying in Scala. They don’t pass a basic test though, multiple parameter lists mean exactly that, lists. A list can have a size of 0, 1, 2 …

def x1(a: Int)(b: Int) = 1
// x1: (a: Int)(b: Int)Int

def x2()(b: Int) = 1
// x2: ()(b: Int)Int

def x3(a: Int)() = 1
// x3: (a: Int)()Int

def x4(a: Int, b: Int)(c: Int) = 1
// x4: (a: Int, b: Int)(c: Int)Int

Let’s define a method with one argument in every list and call it currying. Nay, this doesn’t work either:

def x1(a: Int)(b: Int) = 1

x1(2)
// missing arguments for method x1;
// follow this method with `_' ...

So what this “currying” is good for? Well, it’s a type inference tool. Consider this:

  def foldLeft[B](z: B)(f: (B, A) => B): B = {
     ...
  }

Scala has a local inference, so if I put all parameters into the same list it won’t infer function type in this example:

List(1,2,3).myFoldLeft(0, _ + _)
// Error, can't infer the type of f

List(1,2,3).foldLeft(0)(_ + _)
// No problem here

The reason why the second example works is because Scala checks the first parameter list first and infers that B is Int. So finding type of f becomes simple. In the first case it’s not possible.