Taking and Filtering Lazily
Slice infinite streams.
Taking and Filtering Lazily is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 4 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.
Consuming Lazily
The real value of a LazyList shows when you consume it. Operations like take, filter, and map stay lazy, computing only the cells you ultimately force.
This lesson covers the key transformers and how to stop laziness at the right moment.
take(n)
take(n) returns a LazyList of at most the first n elements, itself still lazy. Nothing is computed until you force the result.
It is the safe way to bound an infinite stream.
object Demo extends App {
val first5 = LazyList.from(1).take(5)
println(first5.toList)
}takeWhile
takeWhile(p) yields elements as long as the predicate holds, then stops at the first failure.
On an infinite stream this is a clean stopping condition that does not require knowing a count.
object Demo extends App {
val small = LazyList.from(1).takeWhile(_ < 6)
println(small.toList)
}drop and dropWhile
drop(n) skips the first n elements; dropWhile(p) skips while the predicate holds. Both return a lazy remainder.
Use them to seek into a stream before taking a slice.
object Demo extends App {
val rest = LazyList.from(1).drop(3).take(3)
println(rest.toList)
}Lazy filter
filter on a LazyList is lazy: it only tests elements as the downstream consumer pulls them.
Combined with take, you process exactly enough source elements to satisfy the demand, no more.
object Demo extends App {
val evens = LazyList.from(1).filter(_ % 2 == 0)
println(evens.take(4).toList)
}Seeing the Demand
Add a print inside the predicate to watch how few elements get tested. The filter runs only until take is satisfied.
Run this: the count of "check" lines shows demand-driven evaluation, not the whole stream.
object Demo extends App {
val r = LazyList.from(1).filter { n =>
println("check " + n); n % 3 == 0
}
println(r.take(2).toList)
}Chaining map and filter
You can chain transformers; the whole pipeline stays lazy and fuses per element. Each source element flows through map then filter only when pulled.
No intermediate full collections are built.
object Demo extends App {
val r = LazyList.from(1).map(_ * _).filter(_ > 10).take(3)
println(r.toList)
}find: Stop Early
find(p) returns the first matching element as an Option, forcing only up to that element. It is perfect for searching an infinite stream.
Once a match is found, evaluation stops immediately.
object Demo extends App {
val firstBig = LazyList.from(1).find(_ * _ > 50)
println(firstBig)
}headOption and exists
headOption safely peeks the first element; exists(p) forces only until a match is found (or forever if none). Use exists on infinite streams only when a match is guaranteed.
Both stop as early as possible.
object Demo extends App {
val has = LazyList.from(1).exists(_ == 7)
println(has)
}Forcing a Result
Lazy transformers describe work; a terminal operation forces it. toList, foreach, sum, and foldLeft consume the (bounded) stream and produce a concrete value.
Always bound an infinite stream before a terminal that needs all of it.
object Demo extends App {
val total = LazyList.from(1).take(100).sum
println(total)
}Pitfalls When Filtering
A filter that matches nothing turns a bounded take into an infinite search. LazyList.from(1).filter(_ < 0).take(1) hangs forever.
Ensure predicates can be satisfied, or pair filtering with takeWhile to guarantee termination.
Quick Check
Check your understanding of lazy consumption.
Recap
take, takeWhile, drop, filter, and map stay lazy and fuse per element; find, exists, and headOption stop early; terminals like toList and sum force the result.
Guard against unsatisfiable filters on infinite streams. You can now generate and consume lazy sequences with confidence.
Frequently asked questions
Is the “Taking and Filtering Lazily” lesson free?
Yes — the full text of “Taking and Filtering Lazily” 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 “Taking and Filtering Lazily”?
Slice infinite streams. 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 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Taking and Filtering Lazily” 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