2010-09-17 19 views
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) 

    } 
} 

Si no pongo un punto y coma o una línea vacía antes println que falla al compilar:¿Por qué este código necesita una línea vacía o un punto y coma?

valor recursiva AFACT necesita tipo

+0

posible duplicado de [¿Por qué la inferencia de punto y coma de Scala falla aquí?] (Http://stackoverflow.com/questions/2246212/why-does-scalas-semicolon-inference-fail-here) – missingfaktor

+0

Factor de @Missing: I don No creo que sea la misma pregunta.Tanto la pregunta es sobre la inferencia del tipo de error, pero aquí tengo un operador unario y una conversión implícita (y no entiendo por qué eliminaste la etiqueta de inferencia de tipo) – onof

+0

@onof: el compilador no sabe que es un operador unario. Para el compilador, es solo un método normal. La pregunta es básicamente la misma: "¿por qué la inferencia de punto y coma falla para esta llamada a método en notación de sufijo?". – missingfaktor

Respuesta

10

Toda esta charla sobre la función recursiva y el tipo es una pista falsa. La gramática de Scala no permite operadores de postfix en ningún otro lugar que no sea el final de una expresión. Esta es la gramática de la que estamos hablando: la sintaxis de las cosas sin ninguna semántica. Aquí está la gramática relevante de las especificaciones:

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]] 

Los únicos dos lugares en los que aparece al lado de PostfixExpr éstos son después de la if en un comunicado case y antes : _* en una lista de argumentos. Entonces, mirando eso, vemos que las únicas cosas que pueden aparecer en el lado derecho del nombre de método de una expresión de postfix es una adscripción de tipo match.

Entonces, ¿qué expresiones finales? Bueno, las expresiones aparecen en muchos lugares de la gramática, por lo que hay muchas cosas que podrían ponerle fin. En este ejemplo particular, la expresión es BlockStat dentro de un Block, por lo que debe terminar con un punto y coma, que se puede inferir o no.

Para inferir este punto y coma, es necesario que la siguiente línea no sea algo que se pueda analizar como otro tipo de expresión. En este caso particular, tenemos esto:

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

Ahora, vamos a reescribir que desde el punto de vista del compilador:

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

Estos literales e identificadores se analizan como sigue:

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

Así que parece una expresión de infijo válida para el compilador mientras analiza su programa. Después, notó que los tipos no coincidían, pero es demasiado tarde para volver atrás y ver si se puede inferir un punto y coma.

6

Porque de lo contrario el ! podría interpretarse como una expresión binaria

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

Lo contrario podría ser probado si el compilador Evaluó ambos tipos de expresiones involucradas, porque existe la posibilidad de una conversión implícita para estos tipos que permitieron la llamada.

Pero dado que el operando correcto implica aFact, el valor es recursivo, Scala no puede determinar su tipo y, por lo tanto, no es la corrección correcta del operador.

¡Debes ser explícito aquí!

+1

pero println es Unidad, ¿por qué no podría darse cuenta de eso? es unario? Y, ¿por qué la línea vacía es suficiente? – onof

+1

Porque no puede decir que no hay una versión binaria. Es ambiguo a menos que sepas los tipos. – Dario

+8

El analizador toma esta decisión antes de conocer los tipos. Agregué una Inspección recientemente a IntelliJ para resaltar posibles problemas como este. Así es como se vería tu código: http://twitpic.com/2p99rl – retronym

1

A continuación código compila bien:

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) 
    } 
} 
Cuestiones relacionadas