14

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)
Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420
  • 1
    More in the parens vs braces wars: http://stackoverflow.com/a/4387118/1296806 but ignore the answer about "inferring braces." – som-snytt Jul 15 '13 at 05:30

3 Answers3

9

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

Community
  • 1
  • 1
som-snytt
  • 39,429
  • 2
  • 47
  • 129
3

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.

Arie Xiao
  • 13,909
  • 3
  • 31
  • 30
  • I can see why you speculate about the braces as you do, but it's actually not hard to look at the spec, cf my answer. – som-snytt Jul 14 '13 at 19:07
  • Your opening statement is still misleading or imprecise. (Do I need parens? Are braces part of anon function syntax?) Probably the best suggestion is just to lose the type because it's inferred. In fact, http://docs.scala-lang.org/style/types.html#function_values – som-snytt Jul 15 '13 at 05:17
1

I've digged into The Scala Language Specification for Scala 2.9, and found out that part 6.23 describes how anonymous function have to be defined:

Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block
Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’
Binding ::= (id | ‘_’) [‘:’ Type]

As you can see Bindings requires to be placed inside two surrounding parenthesis. So if your anonymous function defines the type of the parameter, it has to be defined in this way:

(c:Char) => 1

And the call to map should look like that:

s.toList map((c:Char) => 1)

Also in the same part of reference documentation you can find:

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.

So it says that if anonymous function is defied as last expression in a code block, and has exacly one parameter, you can use abbreviated syntax to define anonymous function - without parenthesis. So, in your case, you can write c:Char => 1 but only when you place it inside a code block {c:Char => 1}. And you can call map function this way:

s.toList map({c:Char => 1})

or with abbreviated syntax without parenthesis:

s.toList map {c:Char => 1}

On the other hand, when we look back at the anonymous function specification:

Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr

We can see that we can define your anonymous function, if we don't want to specify argument type, without parameter binding in those two ways:

s.toList map (c => 1)
// or
s.toList map (_ => 1)

Also, this is summarized in changelog at the end of the documentation (Changes in Version 2.1.7 (19-Jul-2006)):

Closure Syntax

The syntax of closures has been slightly restricted (§6.23). The form

x: T => E

is valid only when enclosed in braces, i.e. { x: T => E }. The following is illegal, because it might be read as the value x typed with the type T => E:

val f = x: T => E

Legal alternatives are:

val f = { x: T => E }
val f = (x: T) => E