Tour of Scala

For Comprehensions

Language

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e, where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter. A comprehension evaluates the body e for each binding generated by the enumerators and returns a sequence of these values.

Here’s an example:

case class User(val name: String, val age: Int)

val userBase = List(new User("Travis", 28),
  new User("Kelly", 33),
  new User("Jennifer", 44),
  new User("Dennis", 23))

val twentySomethings = for (user <- userBase if (user.age >=20 && user.age < 30))
  yield user.name  // i.e. add this to a list

twentySomethings.foreach(name => println(name))  // prints Travis Dennis

The for loop used with a yield statement actually creates a List. Because we said yield user.name, it’s a List[String]. user <- userBase is our generator and if (user.age >=20 && user.age < 30) is a guard that filters out users who are in their 20s.

Here is a more complicated example using two generators. It computes all pairs of numbers between 0 and n-1 whose sum is equal to a given value v:

def foo(n: Int, v: Int) =
   for (i <- 0 until n;
        j <- i until n if i + j == v)
   yield (i, j)

foo(10, 10) foreach {
  case (i, j) =>
    print(s"($i, $j) ")  // prints (1, 9) (2, 8) (3, 7) (4, 6) (5, 5)
}

Here n == 10 and v == 10. On the first iteration, i == 0 and j == 0 so i + j != v and therefore nothing is yielded. j gets incremented 9 more times before i gets incremented to 1. Without the if guard, this would simply print the following:


(0, 0) (0, 1) (0, 2) (0, 3) (0, 4) (0, 5) (0, 6) (0, 7) (0, 8) (0, 9) (1, 1) ...