ScalaSchool

For Comprehensions

Cheat Sheet

for ( i <- 1 to n) println(i)
// i iterates through all values in 1 to n

for {
  i <- 1 to 9
  j <- 1 to 9
} println(s"($i,$j)")
// Multiple iterates

for {
  i <- 1 to 9
  j <- 1 to 9 if i != j
} println(s"($i,$j)")
// Guards

for {
  i <- 1 to 3
  from = 4 - i
  j <- from to 3
} println(s"($i,$j)")
// Variables

val r = for (i <- 1 to n) yield i*i
// r will be a sequence 1, 4, 9, 25, ...

for ( (x,y) <- pairs) println( x + "" y)
// Destructures pairs with extractors

What is a For Comprehensions?

A for-comprehension is syntactic sugar for map, flatMap and filter operations on collections.

The general form is for (s) yield e

Example 1

    // list all combinations of numbers x and y where x is drawn from
    // 1 to M and y is drawn from 1 to N
    for (x <- 1 to M; y <- 1 to N)
      yield (x,y)

is equivalent to

    (1 to M) flatMap (x => (1 to N) map (y => (x, y)))

Translation Rules

A for-expression looks like a traditional for loop but works differently internally

for (x <- e1) yield e2 is translated to e1.map(x => e2)

for (x <- e1 if f) yield e2 is translated to for (x <- e1.filter(x => f)) yield e2

for (x <- e1; y <- e2) yield e3 is translated to e1.flatMap(x => for (y <- e2) yield e3)

This means you can use a for-comprehension for your own type, as long as you define map, flatMap and filter.

For more, see lecture 6.5.

Example 2

    for {  
      i <- 1 until n  
      j <- 1 until i  
      if isPrime(i + j)  
    } yield (i, j)  

is equivalent to

    for (i <- 1 until n; j <- 1 until i if isPrime(i + j))
        yield (i, j)  

is equivalent to

    (1 until n).flatMap(i => (1 until i).filter(j => isPrime(i + j)).map(j => (i, j)))

classes in for comprehensions

For a class to be used in a for comprehension it needs to have the following methods defined:

abstract class CustomClass[T] {
  def map[U](f: T => U): CustomClass[U]
  def flatMap[U](f: T => CustomClass[U]): CustomClass[U]
  def withFilter(p: T => Boolean): CustomClass[T]
}

Whenever a generic class has map, flatMap, and withFilter defined on it (with the signatures as above) then it can be used inside a for comprehension.