2012-03-31 10 views
10

¿Por qué no puedo definir una variable recursivamente en un bloque de código?¿Por qué no puedo definir una variable recursivamente en un bloque de código?

scala> { 
    | val test: Stream[Int] = 1 #:: test 
    | } 
<console>:9: error: forward reference extends over definition of value test 
       val test: Stream[Int] = 1 #:: test 
              ^

scala> val test: Stream[Int] = 1 #:: test 
test: Stream[Int] = Stream(1, ?) 

lazy palabra clave resuelve este problema, pero no puedo entender por qué funciona sin un bloque de código, pero arroja un error de compilación en un bloque de código.

Respuesta

23

Tenga en cuenta que en el REPL

scala> val something = "a value" 

se evalúa más o menos como sigue:

object REPL$1 { 
    val something = "a value" 
} 
import REPL$1._ 

lo tanto, cualquier val (o def, etc.) es un miembro de un objeto interno ayudante REPL .

Ahora el punto es que las clases (y objetos) permiten referencias hacia adelante en sus miembros:

object ForwardTest { 
    def x = y // val x would also compile but with a more confusing result 
    val y = 2 
} 
ForwardTest.x == 2 

Esto no es cierto para val s dentro de un bloque. En un bloque, todo debe definirse en orden lineal. Por lo tanto, val s ya no son miembros sino variables simples (o valores, resp.). Lo siguiente no compila tampoco:

def plainMethod = { // could as well be a simple block 
    def x = y 
    val y = 2 
    x 
} 

<console>: error: forward reference extends over definition of value y 
    def x = y 
      ^

No es recursion lo que hace la diferencia. La diferencia es que las clases y los objetos permiten referencias hacia adelante, mientras que los bloques no.

2

El motivo de este comportamiento depende de diferentes tiempos de inicialización de val. Si escribe val x = 5 directamente en REPL, x se convierte en miembro de un objeto, cuyos valores se pueden inicializar con un valor predeterminado (nulo, 0, 0.0, falso). Por el contrario, los valores en un bloque no pueden inicializarse por valores predeterminados.

Esto tiende a un comportamiento diferente:

scala> class X { val x = y+1; val y = 10 } 
defined class X 

scala> (new X).x 
res17: Int = 1 

scala> { val x = y+1; val y = 10; x } // compiles only with 2.9.0 
res20: Int = 11 

En Scala 2.10 el último ejemplo no se compila más. En 2.9.0, el compilador reordena los valores para que compile. Hay un bug report que describe los diferentes tiempos de inicialización.

+0

El último ejemplo no se compila. (Lo que por supuesto es el punto en cuestión). – Debilski

+0

@Debilski: Tiene razón, con 2.10 ya no compila. Usé 2.9.0 para hacer que compile como se menciona en el informe de error. – sschaef

+0

Estaba usando 2.9.1-1. Así que debe haber sido cambiado en el medio. – Debilski

4

voy a añadir que cuando se escribe:

object O { 
    val x = y 
    val y = 0 
} 

En realidad se está escribiendo esto:

object O { 
    val x = this.y 
    val y = 0 
} 

Esa pequeña this es lo que falta cuando se declara estas cosas dentro de una definición.

0

Me gustaría agregar que una Hoja de trabajo de Scala en el Scala-IDE basado en Eclipse (v4.0.0) no se comporta como REPL como podría esperarse (por ejemplo, https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started dice "Las hojas de trabajo son como una sesión REPL con esteroides ") a este respecto, pero más bien como la definición de un método largo: Es decir, las definiciones de validación referencial val (incluidas las definiciones val recursivas) en una hoja de cálculo deben ser miembros de algún objeto o clase.

Cuestiones relacionadas