在理解中结合列表、未来和选项 - scalaz

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

我遇到了以下问题:

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

我想让这段代码工作(我想每个人都理解这个想法,因为我添加了类型)。

"to work" 我的意思是,我希望继续使用 for-comprehension 结构并最终实现预期的类型。我知道有 "ugly" 种方法可以做到这一点,但我想学习如何纯粹地做到这一点 :)

当我阅读互联网时,我得出的结论是我的问题可以通过 monad 转换器和 scalaz 解决。不幸的是,我找不到一个例子来帮助更好地理解我应该如何进行。

目前我已经尝试过 scalaz 和 Eff monad 库,但我想我仍然不明白它是如何工作的,因为我无法解决我的问题。

如有任何帮助,我将不胜感激。

编辑:它应该是序列的未来,也关于 "whatever" 我把它作为函数的参数,很抱歉误导你

您可以使用 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.: 既然你从一个future开始,你就不能return一个Seq[(T,S)],你只能有一个future。如果你想阻止并得到结果,你必须调用Await.result。

for 理解的问题在于它不是某种神奇的单子 "unwrapper",它只是 mapflatMap 和 [=16= 的序列].

如您所知,mapflatMap 仅在 "inner" 类型上运行,而 "outer" 类型的 monad 保持不变。这意味着你不能这样做:

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

内单for。相反,您可以这样做:

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

看起来有点丑。

回到你的案例,我更容易明确地使用 mapflatMap 编写整个转换,因为它给你更大的可见性和控制:

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

此外,@trustnoone 提到,如果不显式调用 Await.

,就无法摆脱 Future