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

前端 未结 2 547
-上瘾入骨i
-上瘾入骨i 2021-01-20 18:33

I\'ve got following problem:

val sth: Future[Seq[T, S]] = for {
  x <- whatever: Future[List[T]]
  y <- x: List[T]
  z <- f(y): Future[Option[S]]
           


        
相关标签:
2条回答
  • 2021-01-20 18:53

    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.

    0 讨论(0)
  • 2021-01-20 18:57

    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.

    0 讨论(0)
提交回复
热议问题