For-Comprehensions
In this lesson, you will learn about for-comprehensions in Scala.
They provide a clean, readable syntax for working with collections and monadic types
such as Option, Either, and Try.
For-comprehensions are not loops in the traditional sense.
They are a powerful abstraction built on top of map,
flatMap, and withFilter.
Why Use For-Comprehensions?
Without for-comprehensions, chaining multiple transformations can quickly become hard to read due to nested calls.
- Improves readability
- Reduces nesting
- Makes data flow explicit
- Works uniformly across many Scala types
Basic Syntax
A simple for-comprehension iterates over a collection and produces a new one.
val numbers = List(1, 2, 3, 4)
val doubled = for (n <- numbers) yield n * 2
println(doubled)
This is equivalent to using map.
Multiple Generators
You can use multiple generators to create combinations of values.
val letters = List("A", "B")
val numbers = List(1, 2)
val pairs = for {
l <- letters
n <- numbers
} yield (l, n)
println(pairs)
Each generator runs for every value of the previous one.
Using Guards (Filters)
Guards allow you to filter values inside a for-comprehension.
val evenNumbers = for {
n <- List(1, 2, 3, 4, 5, 6)
if n % 2 == 0
} yield n
println(evenNumbers)
Guards are translated into withFilter calls.
For-Comprehensions with Option
For-comprehensions work seamlessly with Option.
If any value is None, the entire result becomes None.
val result = for {
a <- Some(10)
b <- Some(5)
} yield a + b
println(result)
This avoids explicit null or None checks.
For-Comprehensions with Either
You can chain error-prone computations using Either.
def parseInt(s: String): Either[String, Int] =
if (s.forall(_.isDigit)) Right(s.toInt)
else Left("Invalid number")
val result = for {
a <- parseInt("10")
b <- parseInt("2")
} yield a / b
println(result)
If any step fails, the computation short-circuits with a Left.
For-Comprehensions with Try
For-comprehensions also work with Try, making exception handling concise.
import scala.util.Try
val result = for {
a <- Try(10 / 2)
b <- Try(a * 3)
} yield b
println(result)
Desugaring: What the Compiler Does
A for-comprehension is translated into method calls. For example:
for {
x <- list
y <- anotherList
} yield x + y
Is equivalent to:
list.flatMap(x =>
anotherList.map(y => x + y)
)
Understanding this helps you reason about performance and behavior.
When to Use For-Comprehensions
Use for-comprehensions when:
- Chaining multiple dependent operations
- Working with Options, Either, or Try
- Transforming collections cleanly
- Readability matters
📝 Practice Exercises
Exercise 1
Use a for-comprehension to square all numbers in a list.
Exercise 2
Chain two Option values using a for-comprehension.
Exercise 3
Use a guard to filter odd numbers.
✅ Practice Answers
Answer 1
val squares = for {
n <- List(1, 2, 3, 4)
} yield n * n
Answer 2
val result = for {
a <- Some(5)
b <- Some(10)
} yield a + b
Answer 3
val odds = for {
n <- List(1, 2, 3, 4, 5)
if n % 2 != 0
} yield n
What’s Next?
In the next lesson, you will explore Error Handling in Scala and learn best practices for building robust and reliable applications.