Adam Mackler
Adam Mackler

Reputation: 2080

Scala syntax: why is anonymous function parameter type parsed differently using parenthesis versus curly braces?

Given Scala 2.12.6:

val list = List(1)
val x = 2

This works:

list.map ( y => x + y )

returning List[Int] = List(3)

and this works:

list.map ( (y: Int) => x + y )

returning the same value.

Same for this:

list.map { (y: Int) => x + y }

And same for this:

list.map { y: Int => x + y }

Yet this fails:

list.map ( y: Int => x + y )

producing the error:

error: not found: type +
list.map ( y: Int => x + y )
                       ^

Why is Scala thinking the + is meant to indicate a type, and where is this difference between using parenthesis and curly braces documented and explained?

Upvotes: 2

Views: 385

Answers (1)

Andrey Tyukin
Andrey Tyukin

Reputation: 44908

The Section 6.23 about anonymous functions says:

In the case of a single untyped formal parameter, (x) => e can be abbreviated to x => e. If an anonymous function (x: T) => e with a single typed parameter appears as the result expression of a block, it can be abbreviated to x: T => e.

Thus, in a block { ... }, the function literal (y: Int) => x + y can be abbreviated to just y: Int => x + y.

Without the block, the entire Int => x + y-part is treated as type ascription, so the error message actually makes sense. For example, here is a context in which the offending expression becomes valid:

type y = Unit
type x = Unit
type +[A, B] = Int
val y = (i: Int) => 42 + i

val list = List(1)

println(
  list.map ( y: Int => x + y )
) // happily prints `List(43)`.

This is because there are two ys in two separate scopes (one value, one type alias), so that (y: Int => x + y) becomes (y: Int => +[x, y]), and then (y: Int => Int), which is just a type ascription enforcing that value y is indeed of function type Int => Int (which it is, so everything compiles and runs). Here is another similar example.

My suggestion: stick to the slightly more verbose (foo: Foo) => { ... } notation, it will cause fewer surprises for everyone who tries to read and to modify the code. Otherwise there is some risk that

  • argument types in bindings collide with type ascriptions
  • => of the anonymous lambda collides with function type =>
  • arithmetic operation + collides with binary infix type constructor +[_,_]
  • values x, y collide with undefined types x, y.

The fact that same syntax can denote both types and expressions can be somewhat of a double-edged sword.

Upvotes: 4

Related Questions