Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Currying in Scala: multiple parameter lists vs returning a function

When using the following syntax to define functions with currying enabled:

def sum(x: Int)(y: Int)(z: Int) = x + y + z

one still has to suffix any calls to curried calls of sum with _:

sum _
sum(3) _
sum(3)(2) _

otherwise the compiler will complain.

So I resorted to:

val sum = (x: Int) => (y: Int) => (z: Int) => x + y + z

which works without the _.

Now the question: why does the multiple-parameter-lists version require _ in order for currying to kick in? Why aren't the semantics of those 2 versions equivalent in all contexts?

Also, is the latter version somehow discouraged? Does it suffer from any caveats?

like image 531
Erik Kaplun Avatar asked Oct 02 '14 12:10

Erik Kaplun


1 Answers

The reason why those two semantics are different, is that methods and functions are not the same thing.

Methods are full-fledges JVM methods, whereas functions are values (i.e. instance of classes like Function1, Function2 and so on).

So

def sum(x: Int)(y: Int)(z: Int) = x + y + z

and

val sum = (x: Int) => (y: Int) => (z: Int) => x + y + z

may seem identical, but the first is an method, while the second is a Function1[Int, Function1[Int, Function1[Int, Int]]]

When you try to use a method where a function value is expected, the compiler automatically converts it to a function (a process called eta-expansion).

However, there are case in which the compiler doesn't eta-expand the methods automatically, such as the cases you exposed, in which you explicitly want to partially apply it.

Using _ triggers the eta-expansion, so a method is converted to a function, and everybody is happy.

According to the scala specification, you could also annotate the expected type, in which case the expansion is performed automatically:

def sum(x: Int)(y: Int)(z: Int) = x + y + z
val sumFunction: Int => Int => Int => Int = sum

which is the same reason why

def sum(x: Int, y: Int) = x + y
List(1,2,3).reduce(sum)

works, i.e. we're passing a method where a function is explicitly required.

Here's a more in-depth discussion of when scala performs an eta-expansion: https://stackoverflow.com/a/2394063/846273


Concerning the choice of which to adopt, I'll point you to this answer, which is very exhaustive.

like image 187
Gabriele Petronella Avatar answered Sep 28 '22 07:09

Gabriele Petronella