Ответ 1
Верно, что вы могли бы определить ленивое значение, например:
object Lazy {
def apply[A](init: => A): Lazy[A] = new Lazy[A] {
private var value = null.asInstanceOf[A]
@volatile private var initialized = false
override def toString =
if (initialized) value.toString else "<lazy>@" + hashCode.toHexString
def apply(): A = {
if (!initialized) this.synchronized {
if (!initialized) {
value = init
initialized = true
}
}
value
}
}
implicit def unwrap[A](l: Lazy[A]): A = l()
}
trait Lazy[+A] { def apply(): A }
Использование:
val x = Lazy {
println("aqui")
42
}
def test(i: Int) = i * i
test(x)
С другой стороны, наличие lazy
в качестве модификатора, предоставленного языком, имеет то преимущество, что позволяет ему участвовать в принципе равномерного доступа. Я попытался найти для него запись в блоге, но нет ничего, что выходит за рамки getters и seters. Этот принцип на самом деле более фундаментален. Для значений унифицированы: val
, lazy val
, def
, var
, object
:
trait Foo[A] {
def bar: A
}
class FooVal[A](val bar: A) extends Foo[A]
class FooLazyVal[A](init: => A) extends Foo[A] {
lazy val bar: A = init
}
class FooVar[A](var bar: A) extends Foo[A]
class FooProxy[A](peer: Foo[A]) extends Foo[A] {
def bar: A = peer.bar
}
trait Bar {
def baz: Int
}
class FooObject extends Foo[Bar] {
object bar extends Bar {
val baz = 42
}
}
В Scala 2.6 были введены ленивые значения. Существует комментарий Lambda the Ultimate, в котором говорится, что рассуждение может иметь отношение к формализации возможности иметь циклические ссылки:
Циклические зависимости требуют привязки к ленивым значениям. Предельные значения также могут использоваться для обеспечения выполнения этой инициализации компонента в порядке зависимости. Порядок завершения работы компонента, к сожалению, должен быть закодирован вручную
Я не знаю, почему циклические ссылки не могут автоматически обрабатываться компилятором; возможно, были причины сложности или эффективности. A сообщение в блоге от Iulian Dragos подтверждает некоторые из этих предположений.