The scope of a name introduced by a declaration or definition is the whole statement sequence containing the binding. However, there is a restriction on forward references in blocks: In a statement sequence
s[1]...s[n]
making up a block, if a simple name ins[i]
refers to an entity defined bys[j]
wherej >= i
, then for alls[k]
between and includings[i]
ands[j]
,
s[k]
cannot be a variable definition.- If
s[k]
is a value definition, it must belazy
.
Edit: I am not sure Mikaël Mayer's answer actually explained everything. Consider:
object Test {
def main(args: Array[String]) {
println(x)
lazy val x: Int = 6
}
}
Here, the lazy value x
definitely has to be read/evaluated before it is actually defined in the code! Which would contradict Mikaël's claim that lazy evaluation does away with the need to evaluate things before they are defined.
Normally you cannot have this:
val e: Int = 2
val a: Int = b+c
val b: Int = c
val c: Int = 1
val d: Int = 0
because value c is not yet defined at the time of the definition of a. Because a references c, all values between a and c should be lazy so that the dependency is avoided
val e: Int = 2
lazy val a: Int = b+c
lazy val b: Int = c
lazy val c: Int = 1
val d: Int = 0
This in fact translates a, b and c as objects whose value is initialized when it is read, which would be after the declaration, i.e. this would be equivalent to:
val e: Int = 2
var a: LazyEval[Int] = null
var b: LazyEval[Int] = null
var c: LazyEval[Int] = null
a = new LazyEval[Int] {
def evalInternal() = b.eval() + c.eval()
}
b = new LazyEval[Int] {
def evalInternal() = c.eval()
}
c = new LazyEval[Int] {
def evalInternal() = 1
}
val d = 0
where LazyEval
would be something like the following (implemented by the compiler itself)
class LazyEval[T] {
var value: T = _
var computed: Boolean = false
def evalInternal(): T // Abstract method to be overriden
def eval(): T = {
if(computed) value else {
value = evalInternal()
computed = true
value
}
}
}
Edit
vals don't really exist in java. They are local variables or do not exist in computation. Therefore, the declaration of lazy val exists before anything is done. And remember that closures are implemented in Scala. Your block would be rewritten as it:
object Test { def main(args: Array[String]) { // Declare all variables, val, vars. var x: Lazy[Int] = null // No more variables to declare. Lazy/or not variable definitions x = new LazyEval[Int] { def evalInternal() = 6 } // Now the code starts println(x) } }
You're trying to avoid references to entities which are provably uninitialized (or which are maybe uninitialized).
In a block, assignments occur in source order, but in a class template, members can be overridden and initialized early.
For instance,
{ val a = b ; val b = 1 } // if allowed, value of a is undefined
but in a template
class X { val a = b ; val b = 1 } // warning only
val x = new { override val b = 2 } with X
x.a // this is 2
class Y(override val b: Int) extends X // similarly
You also want to avoid this:
locally {
def a = c
val b = 2 // everything in-between must be lazy, too
def c = b + 1
}
Local objects are explicitly the same as lazy vals:
{ object p { val x = o.y } ; object o { val y = 1 } }
Other kinds of forward reference:
{ val x: X = 3 ; type X = Int }
The spec talks about forward references to "entities" -- a "name refers to an entity" -- which elsewhere means both terms and types, but obviously it really means only terms here.
It will let you harm yourself:
{ def a: Int = b ; def b: Int = a; a }
Maybe your mode of self-destruction must be well-defined. Then it's OK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With