Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code need an empty line or a semicolon?

Tags:

scala

case class MyInt(val i : Int) {
    private def factorial(a : Int) : Int = a match {
        case 0 => 1
        case n => (n) * factorial(n-1)
    }
    def ! = factorial(i)
    override def toString = i.toString
}

object MyInt {
    implicit def intToMyInt(x : Int) = MyInt(x)
    implicit def myIntToInt(x : MyInt) = x.i
}
import MyInt._

object Factorial {  
    def main(args: Array[String]): Unit = {
        val a = 5
        val aFact = a!
        println("factorial of " + a + " is " + aFact)

  }
}

If I don't put a semicolon or an empty line before println it fails to compile:

recursive value aFact needs type

like image 381
onof Avatar asked Sep 17 '10 12:09

onof


1 Answers

All this talk about recursive function and type is a red-herring. Scala's grammar does not allow for postfix operators in any other place than the end of an expression. This is the grammar we are talking about: the syntax of things without any semantics. Here is the relevant grammar from the specs:

Expr        ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr
              | Expr1
Expr1       ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] else Expr]
              | ‘while’ ‘(’ Expr ‘)’ {nl} Expr
              | ‘try’ ‘{’ Block ‘}’ [‘catch’ ‘{’ CaseClauses ‘}’]
                [‘finally’ Expr]
              | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ’)’
              | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’)
              | {nl} [‘yield’] Expr
              | ‘throw’ Expr
              | ‘return’ [Expr]
              | [SimpleExpr ‘.’] id ‘=’ Expr
              | SimpleExpr1 ArgumentExprs ‘=’ Expr
              | PostfixExpr
              | PostfixExpr Ascription
              | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’
PostfixExpr ::= InfixExpr [id [nl]]

The only two places where PostfixExpr appears beside these are after the if on a case statement and before : _* on an argument list. So, looking at that, we see that the only things that can appear on the right side of a postfix expression's method name is a type ascription or a match.

So, what end expressions? Well, expressions appears in a lot of places in the grammar, so there's a lot of things that could end it. In this particular example, the expression is a BlockStat inside a Block, so it must end with a semi-colon, which may be inferred or not.

To infer this semi-colon, it is necessary that the next line must not be something that could be parsed as another kind of expression. In this particular case, we have this:

    val aFact = a!
    println("factorial of " + a + " is " + aFact)

Now, let's rewrite that from the point of view of the compiler:

    val id = id id
    id ( stringLit id id id stringLit id id )

These literals and identifiers are parsed like this:

    val id = id id id ( expr )
    val Pattern2 = SimpleExpr1 id SimpleExpr1 ArgumentExprs
    val Pattern2 = InfixExpr
    val Pattern2 = Expr
    val PatDef
    PatVarDef
    Def
    BlockStat

So that looks like a valid infix expression to the compiler as he parsed your program. Afterwards, it noticed the types didn't match, but it's too late to go back and see if a semi-colon could be inferred.

like image 77
Daniel C. Sobral Avatar answered Oct 03 '22 08:10

Daniel C. Sobral