Operations nesting and for comprehensions

for comprehensions are a very nice syntactic sugar in Scala which allows expressing certain patterns in a very clean way. Of course, just like any other feature, it is crucial not to overuse it.

As a rule of thumb, prefer for comprehensions to nested flatMaps and maps, for example, when you need to flatten several collections. for comprehensions are rewritten internally into a nested sequence of method calls, so be careful for using fors in performance-critical code.

Be aware of implications of using for comprehensions for futures. See the section on concurrency for more on that.

Using for comprehensions for different types

for comprehensions may be used with types which have flatMap, map and withFilter methods, and they do not guard you from using different types for each binding clause if it is allowed by typing:

val ints = Vector(1, 2, 4, 5)
val m = Map(1 -> "a", 3 -> "c")

for {
  n <- ints
  s <- m.get(n)
} yield s

This compiles, even though ints is of type Vector[Int] and m.get(n) is of type Option[String]. This is compiled into

ints.flatMap(n => m.get(n))

flatMap on Scala collections allows its argument function to return anything convertible to an iterable, and Option[T] satisfies this requirement. This makes the code harder to understand and, in some subtle cases, may lead to unexpected behavior, in particular, when collections with uniqueness semantics of keys or values (like maps or sets) are used. Therefore, in most cases when you need to iterate over multiple collections in a nested fashion, convert them to a common type. Usually it makes sense to use .iterator for that:

val resultsIterator = for {
  (interface, peers) <- signals.iterator
  (peer, values) <- peers.iterator
  value <- values.iterator
} yield interface -> value

val results = resultsIterator.toMap