When does a Stream need to be lazy?

2019-03-25 03:33发布

问题:

The following are both meant to create a Stream of integers:

val s: Stream[Int] = 1 #:: s.map(_ + 1)

def makeStream = {
  val s: Stream[Int] = 1 #:: s.map(_ + 1)
  s
}

The first is fine; however the makeStream method won't compile:

error: forward reference extends over definition of value s
  val s: Stream[Int] = 1 #:: s.map(_ + 1)
                             ^

It only compiles if we make s a lazy val. Why does it need to be a lazy val in a method, but not outside?

回答1:

Inside a class, a val definition decompiles into an "getter" method that references a hidden class field. These "getter" methods can be self-referential (or rather, the class initializer can reference the "getter") because this is the semantics of Java methods. Note that your "outside" definition of val s is actually wrapped in a hidden class by the REPL (this is how the REPL circumvents the restriction that a val can't be declared at the top level).

Inside a method, the val definition does not decompile into a "getter" method, but rather into the bytecode necessary to generate a value on the stack. A lazy val, on the other hand, always requires a "getter" method which, as such, can be self-referential.