0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Building a LazyList

Construct lazy sequences.

Building a LazyList is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Construction Basics

There are several ways to build a LazyList. The simplest is the apply factory, just like building a List.

But the real power comes from constructors that keep the tail lazy, which we will explore in this lesson.

val ll = LazyList(10, 20, 30)
println(ll.head)

The Empty LazyList

LazyList.empty is the terminator, the lazy equivalent of Nil. Every finite LazyList ends with it.

You use it as the base case when prepending elements with #::.

object Demo extends App {
  val empty = LazyList.empty[Int]
  println(empty.isEmpty)
  println(empty.toList)
}

Prepending with #::

The #:: operator cons a head onto a by-name tail. Build a list right to left, ending with LazyList.empty.

Because #:: takes its tail lazily, nothing past the head is built until forced.

object Demo extends App {
  val ll = 1 #:: 2 #:: 3 #:: LazyList.empty
  println(ll.toList)
}

Watching Laziness

Put a side effect in the tail to see when it runs. Only forcing the tail triggers it.

Run this: "building tail" prints only after you access the second element, not when the LazyList is defined.

object Demo extends App {
  val ll = 1 #:: { println("building tail"); 2 #:: LazyList.empty }
  println("defined")
  println(ll.head)
  println(ll(1))
}

cons Explicitly

Under the hood #:: is LazyList.cons. Both the head and tail are by-name in cons, giving full control over evaluation.

This is useful when defining recursive generators.

val ll = LazyList.cons(1, LazyList.cons(2, LazyList.empty))
println(ll.head)

Recursive Generators

A LazyList can refer to itself. Define a function that produces a head and recursively calls itself for the tail.

Because the tail is by-name, the recursion does not run forever; it pauses until each cell is demanded.

def countFrom(n: Int): LazyList[Int] =
  n #:: countFrom(n + 1)

println(countFrom(5).take(3).toList)

Running a Generator

Let's run a self-referential generator and pull a few values. take limits how many cells are forced.

Without take this would loop forever, so always bound an infinite generator before forcing it.

object Demo extends App {
  def countFrom(n: Int): LazyList[Int] =
    n #:: countFrom(n + 1)
  println(countFrom(1).take(5).toList)
}

From an Iterator

You can wrap an existing collection or iterator. LazyList.from(start) builds an infinite count, and someList.to(LazyList) converts eagerly known data.

These give a lazy view over data you already have or can describe.

object Demo extends App {
  val ll = List(1, 2, 3).to(LazyList)
  println(ll.map(_ * 2).toList)
}

LazyList.iterate

LazyList.iterate(seed)(f) repeatedly applies f to build each next element from the previous one.

It is a clean way to express sequences defined by a step rule, like powers of two.

object Demo extends App {
  val powers = LazyList.iterate(1)(_ * 2)
  println(powers.take(6).toList)
}

LazyList.continually

LazyList.continually(expr) repeats an expression endlessly, re-evaluating it for each element. Handy for constant or randomized streams.

Combine with take to grab a finite slice.

object Demo extends App {
  val zeros = LazyList.continually(0)
  println(zeros.take(4).toList)
}

Choosing a Constructor

Use apply for fixed small lists, #:: or cons for hand-written recursion, iterate for step rules, from for counting, and continually for repetition.

All keep the tail lazy, so the choice is about expressiveness.

Quick Check

Check your grasp of LazyList construction.

Recap

You built LazyLists with apply, empty, #::/cons, iterate, from, and continually.

The by-name tail makes self-referential, potentially infinite generators safe to define. Next we lean into that to create truly infinite streams.

Frequently asked questions

Is the “Building a LazyList” lesson free?

Yes — the full text of “Building a LazyList” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.

What will I learn in “Building a LazyList”?

Construct lazy sequences. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Scala for Backend Engineering & Functional Programming?

No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Building a LazyList” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?

Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Laziness Explained
  2. Building a LazyList
  3. Infinite Streams
  4. Taking and Filtering Lazily
← Back to Scala for Backend Engineering & Functional Programming