2012-03-31 9 views
10

Dlaczego nie można zdefiniować zmiennej rekursywnie w bloku kodu?Dlaczego nie można zdefiniować zmiennej rekursywnie w bloku kodu?

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 kluczowe rozwiązuje ten problem, ale nie mogę zrozumieć, dlaczego to działa bez bloku kodu, ale generuje błąd kompilacji w bloku kodu.

Odpowiedz

23

Zauważ, że w REPL

scala> val something = "a value" 

oceniany jest mniej więcej następująco:

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

Więc każdy val (lub def, etc) jest członkiem wewnętrznego Helper Object REPL .

Teraz chodzi o to, że klasy (obiektów) i pozwalają na ich odniesienia do przodu członków:

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

To nie jest prawdziwe dla val s wewnątrz bloku. W bloku wszystko musi być zdefiniowane w porządku liniowym. Tak więc val s nie są już członkami, ale zwykłymi zmiennymi (lub wartościami, odpowiednio). Następujące elementy nie są kompilowane:

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 
      ^

To nie jest rekurencja, która robi różnicę. Różnica polega na tym, że klasy i obiekty pozwalają na odniesienie do przodu, natomiast bloki nie.

2

Przyczyna tego zachowania zależy od różnych czasów inicjalizacji val. Jeśli wpiszesz val x = 5 bezpośrednio do REPL, x stanie się członkiem obiektu, którego wartości można zainicjować za pomocą wartości domyślnej (null, 0, 0.0, false). Natomiast wartości w bloku nie mogą zostać zainicjowane według wartości domyślnych.

Prowadzi to do innego zachowania:

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 

W Scala 2.10 Ostatni przykład nie kompiluje więcej. W 2.9.0 wartości są porządkowane przez kompilator, aby go skompilować. Jest bug report, który opisuje różne czasy inicjalizacji.

+0

Ostatni przykład nie jest kompilowany. (Co oczywiście dotyczy całego zagadnienia). – Debilski

+0

@Debilski: Masz rację, z 2.10 już się nie kompiluje. Użyłem 2.9.0, aby skompilować to, jak wspomniano w raporcie o błędzie. – sschaef

+0

Używałem 2.9.1-1. Więc musiało zostać zmienione pomiędzy. – Debilski

4

dodam, że kiedy piszesz:

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

jesteś rzeczywiście pisanie to:

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

To trochę this to, czego brakuje, kiedy zadeklarować ten materiał wewnątrz definicji.

0

Chciałbym dodać, że Scala Arkusz w Eclipse opartego Scala-IDE (v4.0.0) nie zachowuje się jak REPL jak można by się spodziewać (np https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started mówi „Arkusze są niczym sesji REPL na sterydach ") pod tym względem, ale raczej jak definicja jednej długiej metody: Oznacza to, że terminujące definicje val (w tym rekurencyjne definicje val) w arkuszu roboczym muszą być członkami jakiegoś obiektu lub klasy.

Powiązane problemy