3

I'm a bit confused by this Scala notation:

List(1, 2, 3).foldLeft(0)((x, acc) => acc+x)

Both "0" and the function are arguments for foldLeft, why are they passed in two adjacent brackets groups? I'd aspect this to work:

List(1, 2, 3).foldLeft(0, ((x, acc) => acc+x))

But it doesn't. Can anyone explain this to me? Also, how and why to declare such a type of function? Thanks

pistacchio
  • 56,889
  • 107
  • 278
  • 420

3 Answers3

8

Scala allows you to have multiple arguments list:

def foo(a: Int)(b: String) = ???
def bar(a: Int)(b: String)(c: Long) = ???

The reason for using such syntax for foldLeft is the way compiler does type inference: already inferred types in the previous group of arguments used to infer types in consecutive arguments group. In case of foldLeft it allows you to drop type ascription next to the (x, acc), so instead of:

List(1, 2, 3).foldLeft(0)((x: Int, acc: Int) => acc+x)

you can write just

List(1, 2, 3).foldLeft(0)((x, acc) => acc+x)
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • 2
    As ever, multiple argument lists and currying are two very different things. – Randall Schulz Nov 07 '13 at 15:50
  • @RandallSchulz - I think your comment would have made more sense on flavian's answer. But yeah—*seriously!*—what's the deal with the Scala community always conflating multiple parameter lists and partial function application with currying??? – DaoWen Nov 08 '13 at 00:06
  • @DaoWen not quite -- [Randall seen previous version which mistakinly referenced currying](http://stackoverflow.com/posts/19839516/revisions) – om-nom-nom Nov 08 '13 at 11:44
  • @om-nom-nom - Ah—I don't know why it didn't occur to me that you might have fixed your answer. – DaoWen Nov 08 '13 at 14:11
  • @RandallSchulz - These comments started me wondering about the distinction between currying and multiple parameter lists. I created [a question](http://stackoverflow.com/questions/19915628/whats-the-difference-between-currying-and-multiple-parameter-lists) about it if you'd like to weigh in. – Matt Malone Nov 11 '13 at 22:05
4

This is an example of multiple parameter lists in Scala. They're really just syntactic sugar for a normal method call (if you look at the class file's method signatures with javap you'll see that when compiled to Java bytecode they're all combined into a single argument list). The reason for supporting multiple parameter lists are twofold:

  1. Passing functions as arguments: Scala will allow you to replace a parameter list that takes a single argument with a function literal in curly braces {}. For example, your code could be re-written as List(1, 2, 3).foldLeft(0) { (x, acc) => acc+x }, which might be considered more readable. (Then again, I'd just use List(1, 2, 3).foldLeft(0)(_+_) in this case...) Being able to use curly braces like this makes it possible for the user to declare new functions that look more like native syntax. A good example of this is the react function for Actors.
  2. Type inference: There are some details of the type inference process (which I admit I don't fully understand) that make it easier to infer the types used in a later list based on the types in an earlier list. For example, the initial z value passed to foldLeft is used to infer the result type (and left argument type) of the function parameter.
DaoWen
  • 32,589
  • 6
  • 74
  • 101
2

Because in Scala you can define function arguments in multiple groups separated by ()

def test(a: String)(b: String)(implicit ev: Something) { }

The most practical scenario is where a context bound or currying is required, e.g. a specific implicit definition available in scope.

For instance, Future will expect an implicit executor. Look here.

If you look at the definition of the foldLeft method, you will see the first argument is an accumulator and the second a function that will be used for currying.

def foldLeft[B](z: B)(op: (B, A) ⇒ B): B

The parentheses thing is a very useful separation of concerns. Also, once you define a method with:

    def test(a: String)(b: String)
You can't call it with: test("a", "b");
flavian
  • 28,161
  • 11
  • 65
  • 105