Infinite Streams
Model endless data safely.
Infinite Streams is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 3 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.
Infinity, Safely
A LazyList can describe an infinite sequence because its tail is never computed until demanded. You only ever materialize the prefix you consume.
This lets you model natural numbers, primes, or sensor readings without bounding them up front.
All Natural Numbers
LazyList.from(1) is the infinite sequence 1, 2, 3, ... You can take any finite prefix.
Forcing the whole thing would never finish, so always slice it with take or stop with a predicate.
object Demo extends App {
val nats = LazyList.from(1)
println(nats.take(5).toList)
}from with a Step
LazyList.from(start, step) counts by an interval. Use it for even numbers, ticks, or any arithmetic progression.
The sequence is infinite but each call to take forces only what you ask for.
object Demo extends App {
val evens = LazyList.from(0, 2)
println(evens.take(5).toList)
}Self-Referential Streams
A famous trick: define a LazyList in terms of itself. The Fibonacci sequence can be written by zipping the stream with its own tail.
This works only because the tail stays unevaluated until each cell is pulled.
lazy val fibs: LazyList[Int] =
0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
// fibs(0)=0, fibs(1)=1, fibs(2)=1 ...Running Fibonacci
Let's force a prefix of that self-referential Fibonacci stream.
Each new element is computed from earlier, already-memoized ones, so the whole thing stays efficient as you pull more values.
object Demo extends App {
lazy val fibs: LazyList[Int] =
0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
println(fibs.take(10).toList)
}iterate for Sequences
LazyList.iterate generates an infinite sequence by a step function. Powers, geometric growth, and state machines fit naturally.
Here each element is triple the previous, forever.
object Demo extends App {
val triples = LazyList.iterate(1)(_ * 3)
println(triples.take(6).toList)
}A Prime Sieve
Infinite streams shine for the Sieve of Eratosthenes. Take a head prime, filter its multiples from the rest, and recurse.
The filter is lazy, so primes are produced one at a time as you consume them.
def sieve(s: LazyList[Int]): LazyList[Int] =
s.head #:: sieve(s.tail.filter(_ % s.head != 0))
val primes = sieve(LazyList.from(2))Running the Sieve
Now pull the first ten primes from that infinite sieve.
Only enough of the underlying number stream is forced to yield ten primes, demonstrating demand-driven computation.
object Demo extends App {
def sieve(s: LazyList[Int]): LazyList[Int] =
s.head #:: sieve(s.tail.filter(_ % s.head != 0))
val primes = sieve(LazyList.from(2))
println(primes.take(10).toList)
}Never Force the Whole Thing
Methods that need the entire sequence, like length, toList on an unbounded stream, or foreach without a stop, will hang on an infinite LazyList.
Always bound first with take, takeWhile, or find.
// DON'T: LazyList.from(1).toList // hangs forever
val ok = LazyList.from(1).take(3).toListThe Head-Holding Trap
If a val holds the head of an infinite memoizing LazyList and you consume far into it, every forced cell stays alive, leaking memory.
For long traversals, consume via a method without binding the head, or use an Iterator.
Why Infinite Streams Matter
Infinite streams let you separate generation from consumption. The producer describes an endless rule; the consumer decides how much to realize.
This is a powerful functional pattern for pipelines, simulations, and lazy data sources.
Quick Check
Test your understanding of infinite LazyLists.
Recap
Infinite LazyLists work because the tail is demand-driven: from, iterate, self-referential fibs, and the prime sieve all generate endlessly yet realize only what you consume.
Avoid whole-sequence operations and head-holding. Next, we master taking and filtering lazily.
Frequently asked questions
Is the “Infinite Streams” lesson free?
Yes — the full text of “Infinite Streams” 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 “Infinite Streams”?
Model endless data safely. 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 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Infinite Streams” 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
- Laziness Explained
- Building a LazyList
- Infinite Streams
- Taking and Filtering Lazily