0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

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

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