2012-03-31 48 views
10

爲什麼我不能在代碼塊中遞歸地定義一個變量?爲什麼我不能在代碼塊中遞歸地定義一個變量?

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關鍵字解決了這個問題,但我不明白爲什麼它的工作原理沒有一個代碼塊,但在一個代碼塊拋出一個編譯錯誤。

回答

23

注意,在REPL

scala> val something = "a value" 

評價或多或少如下:

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

因此,任何val(或def,等等)是一個內部REPL輔助對象的成員。

現在一點是類(和對象)允許向前引用他們的成員:

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

這不是val個塊內如此。在塊中,一切都必須按照線性順序來定義。因此val s不再是成員,而是普通變量(或值,分別)。以下不編譯:

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 
      ^

這是不是遞歸,這是不同的。區別在於類和對象允許前向引用,而塊不允許。

2

此行爲的原因取決於不同的val初始化時間。如果直接鍵入val x = 5到REPL,則x將成爲對象的成員,可以使用默認值(null,0,0.0,false)初始化哪些值。相反,塊中的值不能通過默認值進行初始化。

這往往不同的行爲:

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 

在斯卡拉2.10最後的例子不能編譯了。在2.9.0中,值由編譯器重新排序以使其編譯。有一個bug report它描述了不同的初始化時間。

+0

最後一個例子不能編譯。 (當然這是整個問題。) – Debilski 2012-03-31 11:52:54

+0

@Debilski:你說得對,用2.10就不能編譯了。我使用2.9.0來獲得這個編譯錯誤報告中提到的。 – sschaef 2012-03-31 12:12:50

+0

我正在使用2.9.1-1。所以它必須在兩者之間改變。 – Debilski 2012-03-31 13:12:00

4

我要補充的是,當你寫:

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

你實際上寫這個:

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

那個小this是當你聲明的定義裏面這個東西缺什麼。

0

我想補充一點,基於Eclipse的Scala-IDE(v4.0.0)中的Scala工作表並不像預期的那樣表現REPL(例如https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started表示「工作表就像是類固醇上的REPL會話「)在這方面,而是像一個長方法的定義:也就是說,工作表中的向前引用val定義(包括遞歸val定義)必須成爲某個對象或類的成員。

相關問題