When you write
{ i: Int => 2 * i }
the braces are a block, and the thing inside is the result expression of the block.
The syntax is the ResultExpr here. Note that a type is allowed after the param name, as above.
That's different from an Expr where it is not.
Here's an example of the difference:
scala> val is = List(1,2,3)
is: List[Int] = List(1, 2, 3)
The first statement in block has a function literal, an Expr requiring parens if we specify the type. The last statement is the result expression of the block, and no parens are required:
scala> is map { val f = (i: Int) => 2*i; i: Int => 2*f(i) }
res0: List[Int] = List(4, 8, 12)
Sometimes you don't need to specify the type because it is inferred from the expected type, and with no type in the Expr, you can omit the parens:
scala> is map { val f: Int=>Int = i => 2*i; i: Int => 2*f(i) }
The Bindings in these productions (in the spec) is your ordinary list of params in parens, which maybe have types. With more than one param, you have to supply parens:
scala> is reduce { val f = (i:Int,j:Int) => i+j; (i:Int,j:Int) => 2*f(i,j) }
res2: Int = 18
Sometimes you'll see a big block of code as an argument to a function. That's a big function literal as the result expression of a block, which is why you see the parameter sitting out in front, with no parens or braces:
scala> is map { i => val j = 2 * i; /* lots of LOC */ ; j }
res7: List[Int] = List(2, 4, 6)
That is different from the following code, which is block of many lines of code with the function literal at the very end. The function just returns j
, or 2:
scala> is map { val j = 2; /* lots of LOC */ ; _ => j }
res8: List[Int] = List(2, 2, 2)
So we know that you can't write the following:
scala> is map (is: Int => 2)
<console>:1: error: identifier expected but integer literal found.
is map (is: Int => 2)
^
What sort of identifier would be meaningful in this context? How about:
scala> is map (is: Int => Int)
which yields the delightful result (spoiler alert):
java.lang.IndexOutOfBoundsException: 3
This works:
scala> val js = List(0,1,2)
js: List[Int] = List(0, 1, 2)
scala> js map (js: Int => Int)
res0: List[Int] = List(0, 1, 2)
The js
in parens is just a value, obviously, not a param, and the type is a type ascription. The value can be a post-fix expression, so this works (or rather, compiles with a feature warning about the post-fix operator syntax):
scala> js map (js init: Int => Int)
warning: there were 1 feature warning(s); re-run with -feature for details
java.lang.IndexOutOfBoundsException: 2
More explanation at this answer:
https://stackoverflow.com/a/13873899/1296806
There is a category of confusion caused by thinking that parens and curly braces are somehow exchangeable. But I found it clarifying to see braces as BlockExprs
, which is what they are. Then it's easier to remember, for instance, that when you use braces in a function application, there is no magic: you've simply supplied a block.
A block is a bunch of side-effecting statements followed by a result statement. That's obvious for functional programmers, perhaps. But it clarifies that the last thing in your braces is a ResultExpr
.
(Footnote: braces for class bodies are different, of course.)