简体   繁体   中英

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

i and io must be of the same monad:

io <- i   // i is an Iterator[...]
lst <- io // io is an IO[List[String]]

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 (eg 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]]

When using IO and for-comprehensions, one option is to use .unsafeToFuture in your for comprehension. In your example, this would be:

val ii: Iterator[IO[List[String]]] = for{ 
  io <- i;
  lst <- io.unsafeToFuture()
} yield lst

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM