5
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

onof
  • 17,167
  • 7
  • 49
  • 85
  • possible duplicate of [Why does Scala's semicolon inference fail here ?](http://stackoverflow.com/questions/2246212/why-does-scalas-semicolon-inference-fail-here) – missingfaktor Sep 17 '10 at 12:52
  • @Missing Factor: I don't think it's the same question. Both the question are about type inference failing but here I have a unary operator and implicit conversion (and i don't understand why you removed type-inference tag) – onof Sep 17 '10 at 13:04
  • @onof: The compiler doesn't know it's a unary operator. For the compiler, it's just a normal method. The question is basically the same: "why semicolon inference fails for this method call in suffix notation?". – missingfaktor Sep 17 '10 at 13:11
  • @onof: _"why you removed type-inference "_ << Because the question is about semicolon inference, not type inference. – missingfaktor Sep 17 '10 at 13:11
  • @Missing Factor: but here the compiler can not recognize the type of aFact, it does not output " illegal start of simple expression". – onof Sep 17 '10 at 13:16
  • @onof: The parser is confused into thinking that `aFact` is a recursive definition. Hence such weird error. – missingfaktor Sep 17 '10 at 13:19
  • 1
    @onof: The two lines are getting parsed like this: `val aFact = a.!(println("factorial of " + a + " is " + aFact))` – missingfaktor Sep 17 '10 at 13:20
  • @Missing Factor: but the error is not the same. I think that, when facing this one, is not so trivial realizing that: the error is by the parser and that the compiler is confused into thinking that aFact is a recursive definition. – onof Sep 17 '10 at 13:25
  • 1
    @onof: I don't know how else to explain it you. Maybe these two code snippets will help: http://ideone.com/wM13l, http://ideone.com/4PPx1. – missingfaktor Sep 17 '10 at 13:35
  • @Missing Factor you could add those snippets in an answer, if you find this question so trivial. I'm sorry this error is caused by the same reason of the error you ask about – onof Sep 18 '10 at 22:39
  • onof, you've accepted the wrong answer. – James Iry Mar 08 '11 at 22:50
  • @James I got the point. And [here](http://stackoverflow.com/questions/4527902/what-is-the-order-of-the-scala-compiler-phases) I found more infos about. Thank you. – onof Mar 09 '11 at 08:11

3 Answers3

10

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.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
6

Because otherwise the ! could be interpreted as a binary expression

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

The opposite could just be proven if the compiler evalated both types of the expressions involved, because there is the possibility of an implicit conversion for these types that allowed the call.

But since the right operand involves aFact itself, the value is recursive, Scala can't determine it's type and thus not the correct fixity of the operator.

You need to be explicit here!

Dario
  • 48,658
  • 8
  • 97
  • 130
  • 1
    but println is Unit, why could not it realize that ! is unary? And, why empty line is enough? – onof Sep 17 '10 at 12:46
  • 1
    Because he can't tell there is no binary version. It's ambiguous unless you know the types. – Dario Sep 17 '10 at 12:55
  • 8
    This decision is made by the parser, before the types are known. I added an Inspection recently to IntelliJ to highlight potential problems like this here's how your code would look: http://twitpic.com/2p99rl – retronym Sep 17 '10 at 13:11
  • There might be an implicit conversion from `Unit` to a type which defines binary `!`. – Alexey Romanov Sep 17 '10 at 13:41
  • 3
    This is the wrong, wrong, wrong answer. It has nothing to do with typing. Parsing happens entirely before typing. Daniel Sobral's answer is right. – James Iry Mar 08 '11 at 22:49
1

Below code compiles fine:

package it.onof.scalaDemo

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:Int = a.!
        println("factorial of " + a + " is " + aFact)
  }
}
Jaydeep Patel
  • 2,394
  • 1
  • 21
  • 27