A smart constructor is a constructor-like function that creates a value while adding an invariant, changing the signature, or returning an abstraction’s base type instead of a concrete data-constructor type. The real constructor remains available for pattern matching, while ordinary construction goes through the smarter interface. section 5.2.1 section 5.6
LazyList uses a smart cons constructor to control evaluation. Its by-name head and tail arguments become memoized lazy values, so forcing either computation once caches the result for later forces. This avoids requiring callers to create thunks or perform caching manually. section 5.2.1
Memoizing a lazy Cons node
Direct construction can recompute work: val x = Cons(() => expensive(x), tl). A smart constructor instead accepts by-name arguments, stores them in lazy vals, and constructs Cons(() => head, () => tail). Repeated access then returns cached values. section 5.2.1
def cons[A](hd: => A, tl: => LazyList[A]): LazyList[A] = lazy val head = hd lazy val tail = tl Cons(() => head, () => tail)
Inspect the complete cons definition, including by-name parameters, lazy caching, and construction of the underlying Cons node. section 5.2.1
Common smart-constructor responsibilities include:
- Enforce an additional invariant or validation rule. section 5.2.1
- Expose a different signature, such as by-name parameters for deferred evaluation. section 5.2.1
- Return the abstraction’s base type, such as LazyList[A], rather than Cons[A] or Empty. section 5.6


