2012-03-31 3 views
10

Tại sao tôi không thể định nghĩa một biến đệ quy trong một khối mã?Tại sao tôi không thể định nghĩa một biến đệ quy trong một khối mã?

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 từ khóa giải quyết vấn đề này, nhưng tôi không thể hiểu tại sao nó hoạt động mà không có một khối mã nhưng ném một lỗi biên dịch trong một khối mã.

Trả lời

23

Lưu ý rằng trong REPL

scala> val something = "a value" 

được đánh giá nhiều hơn hoặc ít hơn như sau:

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

Vì vậy, bất kỳ val (hoặc def, vv) là thành viên của một đối tượng helper REPL nội .

Bây giờ vấn đề là các lớp học (và các đối tượng) cho phép chuyển tiếp tài liệu tham khảo về các thành viên của họ:

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

Đây không phải là đúng đối với val s bên trong một khối. Trong một khối tất cả mọi thứ phải được định nghĩa theo thứ tự tuyến tính. Do đó, val không còn là thành viên nữa mà là các biến đơn giản (hoặc giá trị, resp.). Phần sau không biên dịch:

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 
      ^

Nó không phải là đệ quy tạo nên sự khác biệt. Sự khác biệt là các lớp và các đối tượng cho phép các tham chiếu chuyển tiếp, trong khi các khối thì không.

2

Lý do cho hành vi này phụ thuộc vào thời gian khởi tạo val khác nhau. Nếu bạn nhập val x = 5 trực tiếp vào REPL, x sẽ trở thành thành viên của đối tượng, giá trị nào có thể được khởi tạo với giá trị mặc định (null, 0, 0,0, false). Ngược lại, các giá trị trong một khối không thể được khởi tạo bởi các giá trị mặc định.

này có xu hướng hành vi khác nhau:

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 

Trong Scala 2.10 ví dụ cuối cùng không biên dịch nữa. Trong 2.9.0 các giá trị được sắp xếp lại bởi trình biên dịch để làm cho nó biên dịch. Có một số bug report mô tả các lần khởi tạo khác nhau.

+0

Ví dụ cuối cùng không biên dịch. (Tất nhiên là toàn bộ vấn đề được đề cập đến.) – Debilski

+0

@Debilski: Bạn nói đúng, với 2,10 nó không biên dịch nữa. Tôi đã sử dụng 2.9.0 để có được điều này để biên dịch như đã đề cập trong báo cáo lỗi. – sschaef

+0

Tôi đã sử dụng 2.9.1-1. Vì vậy, nó phải được thay đổi ở giữa. – Debilski

4

Tôi sẽ thêm rằng khi bạn viết:

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

Bạn đang thực sự viết này:

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

Đó chút this là bước còn thiếu khi bạn khai báo cụ này bên trong một định nghĩa.

0

Tôi muốn thêm rằng một Bảng tính Scala trong Eclipse dựa trên Scala-IDE (v4.0.0) không hoạt động như REPL như người ta có thể mong đợi (ví dụ: https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started nói "Bảng tính giống như một phiên REPL trên steroid ") về khía cạnh này, nhưng giống như định nghĩa của một phương pháp dài: Nghĩa là, các tham chiếu chuyển tiếp định nghĩa val (bao gồm các định nghĩa val đệ quy) trong một trang tính phải được thực hiện thành viên của một số đối tượng hoặc lớp.