7

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?

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 3
    http://stackoverflow.com/questions/4915027/two-ways-of-currying-in-scala-whats-the-use-case-for-each/4916606#4916606 – Debilski Oct 02 '14 at 12:51

1 Answers1

8

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.

Community
  • 1
  • 1
Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • I just fail to see why the compiler would require `_` if I do stuff like `val x = foo _` — does it think I might have forgotten to pass the right number of arguments to `foo`? As to the rest of your answer, I'm already aware of all the points :) – Erik Kaplun Oct 02 '14 at 16:04
  • That's a rather arbitrary design decision. From the scala specification you can either pass `_` or explicitly annotate the type (`val x: Int => Int => Int => Int = sum`). Here's a relevant discussion: http://stackoverflow.com/questions/2363013/in-scala-why-cant-i-partially-apply-a-function-without-explicitly-specifying-i – Gabriele Petronella Oct 02 '14 at 16:09