For comprehensions make multi-step logic over effect types read like sequential code.
A for comprehension with yield is syntax sugar for calls to flatMap, map and withFilter. The compiler rewrites
for a <- fa; b <- fb(a) yield f(a, b)into fa.flatMap(a => fb(a).map(b => f(a, b))). Any type with those methods works, which is why the same syntax handles collections, Option, Either, Try, Future and effect types like Cats Effect's IO.
With Option and Either it expresses "do these steps in order and stop at the first failure": each <- unwraps a value, and if any step yields None or Left, the whole expression short-circuits to it.
Other parts:
if condguards filter (requireswithFilter;Eitherdoesn't support guards without an empty value).x = exprbinds an intermediate value without unwrapping.- With collections, multiple generators produce a cartesian product.
All generators in one comprehension must be of the same kind: you can't mix Option and List steps directly; convert first (opt.toList, opt.toRight(err)).
Without yield, a for loop desugars to foreach and returns Unit.
Example
case class User(id: Int, name: String, managerId: Option[Int])
val users = Map(1 -> User(1, "Ana", None), 2 -> User(2, "Bo", Some(1)))
def managerName(id: Int): Option[String] =
for
user <- users.get(id)
mgrId <- user.managerId
manager <- users.get(mgrId)
yield manager.name
println(managerName(2)) // Some(Ana)
println(managerName(1)) // None (Ana has no manager)
def toAge(s: String): Either[String, Int] = s.toIntOption.toRight(s"bad: $s")
val total = for
a <- toAge("30")
b <- toAge("x")
sum = a + b
yield sum
println(total) // Left(bad: x)
val pairs = for n <- List(1, 2, 3); c <- List('a', 'b') if n != 2 yield s"$n$c"
println(pairs) // List(1a, 1b, 3a, 3b)Remember
for ... yielddesugars to flatMap/map/withFilter- Over Option/Either it short-circuits at the first None/Left
- All generators must be the same kind of type
x = exprbinds a plain intermediate value
Watch out for
- Mixing an Option generator into a List comprehension (or Future with Option) and getting confusing type errors.