2
votes

Below is the definition of Stream type taken from fp in scala chapter 5

sealed trait Stream[+A]
case object Empty                                   extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

We can write the following method that will return an infinite stream of a

def constant[A](a: A): Stream[A] = {
    lazy val x: Stream[A] = Cons(() => a, () => x)
    x
  }

What I do not quite understand is, why the compiler doesn't throw forward reference extends over definition of value when x is defined as lazy val (otherwise it does)?

I found an old post here: https://www.scala-lang.org/old/node/6502 but still looking for a clear explanation.

2

2 Answers

4
votes

Because now, due exactly to the post you linked (AFAIK), it's specifically allowed:

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 s1…sn making up a block, if a simple name in si refers to an entity defined by sj where j≥i, then for all sk between and including si and sj,

  • sk cannot be a variable definition.

  • If sk is a value definition, it must be lazy.

0
votes

I won't get into much details, maybe that would be sufficient for you... It works just as recursive functions do. Lazy vals behave like memoized functions.

Moreover, you could also make it work as a val, but it cannot be a local variable. See that

class Test {
   val a = 5
   val x: Stream[Int] = Cons(() => a, () => x)
}

compiles just fine.