scalaioscalaz

For comprehension with IO-monad in Scala


I have the following in my Scala

import scalaz.effect.IO
val i: Iterator[IO[List[String]]] = null

val ii: Iterator[IO[List[String]]] = for{ //This does not compile
  io <- i;
  lst <- io
} yield lst

Why? What's wrong?

I expected the ii is completely the same as i. But it refuses to compile:

Error:(12, 11) type mismatch;
 found   : scalaz.effect.IO[List[String]]
 required: scala.collection.GenTraversableOnce[scalaz.effect.IO[List[String]]]
      lst <- io

Solution

  • flatMap in scala

    Recall that for-comprehensions are just sugared calls to flatMap:

    for {
      a <- expr  //expr must return M[A] such that M has a 
                 //flatMap method: flatMap[B](f: A => N[B]) for some N
    
      b <- f(a)  //f(a) must be an N[B]
      ...
    

    Your question

    Here is the signature of Iterator.flatMap

    def flatMap[B](f: A => GenTraversableOnce[B]): Iterator[B]
    

    But you were attempting to supply a function returning an IO[B]:

    lst <- io //You cannot just have `io` on the right here
    

    hence the compilation error.

    flatMap in scala again

    Scala's flatMap <~> for-comprehension conversions as they apply to collection types (and Option) are (in my opinion) confusing as they allow you to switch between different types of Monad (e.g. List/Option/Set etc). For example, what is the type of x here?

    val x = 
      for  {
        i <- List(1, 2, 3)
        j <- Option(i + 1)
        k <- Stream(i, j)
      } yield k
    

    Monads in scalaz

    Having a closer look at scalaz.Monad's flatMap:

    trait Monad[M[_]] {
      def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B]
    }
    

    The type of M is always fixed. That is, in this comprehension:

    lazy val ma: M[A] = ???
    for (a <- ma; b <- f(a)) yield b
    

    The result type of the function f must be in M[B] for some B. Whilst sometimes this can be a little irritating, it has the advantage of being completely predictable. You never get confused about what monad your for comprehension is in.

    Back to the question

    It is not obvious what you want, but here are a few suggestions:

    i.toStream.sequence.map(flatten) //IO[Stream[String]]