Combining List, Future and Option in for-comprehension - scalaz

旧城冷巷雨未停 提交于 2019-12-01 20:51:20

You could do something like what you need using the scalaz ListT monad transformer

 object Test {
   import scalaz._
   import ListT._
   type T = String
   type S = Int
   val whatever: Future[List[T]] = ??? // you get this somewhere
   def f(y: T): Future[Option[S]] = ??? // function that returns future of option

   val sth: Future[List[(T, S)]] = (for {
     y <- listT(whatever) 
     // you cannot mix list and option, but you can convert the option to a list of 1 item
     n <- listT(f(y).map(_.toList)) 
   } yield y -> n).run
 }

N.B.: Since you start with a future, you cannot return a Seq[(T,S)], you can only have a future. You have to call Await.result if you want to block and get the result.

The problem with for comprehension is that it's not some kind of magic monadic "unwrapper", it's just a sequence of map, flatMap and filter.

As you may know map and flatMap operate only on "inner" type, leaving "outer" type of monad unchanged. This means you can't do this:

for {
  x <- whatever: Future[List[T]]
  y <- x: List[T]
} yield y

inside single for. Instead, you can do something like this:

for (x <- whatever: Future[List[T]])
  yield for (y <- x: List[T]) yield y

Which looks kinda ugly.

Back to your case, I's easier to write whole transformation explicitly using map and flatMap, as it gives you greater visibility and control:

whatever.flatMap {
  x: List[T] =>
    Future.sequence(x.map {
      y: T => f(y).map(y -> _)
    }).map(_.collect {
      case (y, Some(n)) => y -> n
    })
}

Also, @trustnoone mentioned, you can't get rid of the Future without explicitly calling Await.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!