Почему я не могу определить переменную рекурсивно в блоке кода?
Почему я не могу определить переменную рекурсивно в кодовом блоке?
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
ключевое слово решает эту проблему, но я не могу понять, почему он работает без кода, но генерирует ошибку компиляции в блоке кода.
Ответы
Ответ 1
Обратите внимание, что в 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
больше не являются членами, а просто переменными (или значениями, соответственно). Не компилируется также следующее:
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
Я добавлю, что когда вы пишете:
object O {
val x = y
val y = 0
}
Вы на самом деле пишете это:
object O {
val x = this.y
val y = 0
}
Это маленькое this
- это то, чего не хватает, когда вы объявляете этот материал внутри определения.
Ответ 3
Причина такого поведения зависит от времени инициализации валидаров. Если вы введете 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
В Scala 2.10 последний пример больше не компилируется. В 2.9.0 значения переупорядочиваются компилятором для его компиляции. Существует отчет об ошибке который описывает различные времена инициализации.
Ответ 4
Я хотел бы добавить, что Scala Рабочий лист в Scala -IDE (v4.0.0) на основе Eclipse не ведет себя как REPL, как можно было бы ожидать (например, https://github.com/scala-ide/scala-worksheet/wiki/Getting-Started говорит, что "Рабочие листы похожи на сеанс REPL на стероидах" ) в этом отношении, а скорее как определение одного длинного метода: То есть, прямые ссылки на определения val (включая рекурсивные val) в листе должны быть сделаны членами какого-либо объекта или класса.