Lazy-list Cons is the nonempty LazyList constructor. Unlike strict List Cons, it stores the head and tail as explicit thunks: () => A and () => LazyList[A]. The values are evaluated only when demanded, so constructing a Cons does not evaluate its tail. section 5.2
Pattern matching exposes the thunks rather than their values. Reading the head therefore requires h(), while traversing the tail requires t(). The cons helper accepts by-name arguments, memoizes them with lazy val, and wraps them in delayed functions. section 5.2
The constructor’s evaluation behavior shapes common operations:
- Taking one element avoids forcing the original tail. section 5.6
- Taking zero elements forces neither head nor tail. section 5.6
- Recursive tails can remain suspended, supporting stack-safe lazy construction. section 5.6
A delayed infinite sequence
def from(n: Int): LazyList[Int] = cons(n, from(n + 1))
Listing 5.2 A simple definition for LazyList
enum LazyList[+A]:
case Empty
case Cons(h: () => A, t: () => LazyList[A]) #1
object LazyList:
def cons[A](
hd: => A, tl: => LazyList[A]
): LazyList[A] = #2
lazy val head = hd #3
lazy val tail = tl
Cons(() => head, () => tail)
def empty[A]: LazyList[A] = Empty #4
def apply[A](as: A*): LazyList[A] = #5
if as.isEmpty then empty
else cons(as.head, apply(as.tail*))
Inspect the complete LazyList definition, including Cons, the by-name cons helper, memoized head and tail values, empty, and apply. section 5.2
