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

后端 未结 3 1522
情歌与酒
情歌与酒 2021-01-01 21:21

Why does the Scala 2.11.0-M3 compiler give me error: identifier expected but integer literal found. when the round brackets () are use

相关标签:
3条回答
  • 2021-01-01 21:31

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

    0 讨论(0)
  • 2021-01-01 21:39

    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
    
    0 讨论(0)
  • 2021-01-01 21:49

    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.

    0 讨论(0)
提交回复
热议问题