Search code examples
scalafunctional-programmingfor-comprehension

Converting a sequence of map operations to a for-comprehension


I read in Programming in Scala section 23.5 that map, flatMap and filter operations can always be converted into for-comprehensions and vice-versa.

We're given the following equivalence:

def map[A, B](xs: List[A], f: A => B): List[B] =
  for (x <- xs) yield f(x)

I have a value calculated from a series of map operations:

val r = (1 to 100).map{ i => (1 to 100).map{i % _ == 0} }
                  .map{ _.foldLeft(false)(_^_) }
                  .map{ case true => "open"; case _ => "closed" }

I'm wondering what this would look like as a for-comprehension. How do I translate it?

(If it's helpful, in words this is:

  • take integers from 1 to 100
  • for each, create a list of 100 boolean values
  • fold each list with an XOR operator, back into a boolean
  • yield a list of 100 Strings "open" or "closed" depending on the boolean

I imagine there is a standard way to translate map operations and the details of the actual functions in them is not important. I could be wrong though.)


Solution

  • Is this the kind of translation you're looking for?

    for (i <- 1 to 100;
         val x = (1 to 100).map(i % _ == 0);
         val y = x.foldLeft(false)(_^_);
         val z = y match { case true => "open"; case _ => "closed" })
      yield z
    

    If desired, the map in the definition of x could also be translated to an "inner" for-comprehension.

    In retrospect, a series of chained map calls is sort of trivial, in that you could equivalently call map once with composed functions:

     s.map(f).map(g).map(h) == s.map(f andThen g andThen h)
    

    I find for-comprehensions to be a bigger win when flatMap and filter are involved. Consider

    for (i <- 1 to 3;
         j <- 1 to 3 if (i + j) % 2 == 0;
         k <- 1 to 3) yield i ^ j ^ k
    

    versus

    (1 to 3).flatMap { i =>
      (1 to 3).filter(j => (i + j) % 2 == 0).flatMap { j =>
        (1 to 3).map { k => i ^ j ^ k }
      }
    }