Why does the Scala 2.11.0-M3
compiler give me error: identifier expected but integer literal found.
when the round brackets ()
are used while it compiles fine with curly brackets {}
?
$ scala
Welcome to Scala version 2.11.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val s = "this is a string"
s: String = this is a string
scala> s.toList map (c:Char => 1)
<console>:1: error: identifier expected but integer literal found.
s.toList map (c:Char => 1)
^
scala> s.toList map {c:Char => 1}
res7: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
Somehow it compiles fine with the round brackets as well when the left-hand side of the anonymous function is within another pair of the round brackets. Why?
scala> s.toList map ((c:Char) => 1)
res8: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
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.)
When you write an expression c:Char => 1
, Scala compiler treats it as c:(Char => 1)
, that is, a function variable c
with type Char => 1
. However, 1
is not a valid type (or type variable), so Scala compiler complains. So you need to write s.toList map ((c:Char) => 1)
to help the compiler out of the type association.
{c:Char => 1}
creates a function literal, that is, a function that accepts a char and returns 1. So
s.toList map {c:Char => 1}
is the same as
s.toList map({c:Char => 1})
// or
val fn = {c: Char => 1}
s.toList map fn
However, it seems that Scala doesn't have special specification for the {c:Char => blabla}
form for creating function literals.
I think it is because that there is {some_var: SomeType => *Class Definition* }
form in class definition to reference this
with some_var
and add lower bound of the type of this
. This causes the Scala lexer to treat a single typed parameter after {
to be formal parameter and treat the statements after =>
to be actual body. Note that you CANNOT create a function with 2 parameters like this {c: Char, d:Char => 1}
.
UPDATE: As commented by som-snytt, it is result expression that causes the brace version to be evaluated to a function result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With