Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala compiler says "error: identifier expected but integer literal found." for () not {}

Tags:

scala

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)
like image 344
Jacek Laskowski Avatar asked Jul 14 '13 13:07

Jacek Laskowski


Video Answer


2 Answers

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.)

like image 190
som-snytt Avatar answered Sep 19 '22 06:09

som-snytt


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.

like image 36
Arie Xiao Avatar answered Sep 20 '22 06:09

Arie Xiao