如果您使用的是scalaZ或cat,您可以使用更通用的方法,这将适用于更多类型(Future
需要有Traverse
typeclass,并且Option
只需要是 a Monad
)
第一种方法是:
import scalaz.std.scalaFuture.futureInstance
import scalaz.std.option.optionInstance
import scalaz.syntax.traverse._
val fofo: Future[Option[Future[Option[Int]]]] = Future(Option(Future(Option(5))))
val ffoo: Future[Future[Option[Option[Int]]]] = fofo.map(_.sequence)
val fo = fofo.map(_.sequence).join.map(_.join) // Future(Some(5))
在这里,您首先在 future 中“交换”类型sequence
,然后将它们连接在一起。因此,对于任何两个更高种类的类型F[_]
和G[_]
,其中F
- 有一个Traverse
实例并且G
是Monad
- 你可以这样做。更改Future
为List
,实现不会改变。
另一种可能很有趣的方法是使用 monad 转换器。想法是,在 Monad 你可以将类型连接在一起F[F[A]] => F[A]
。
Future(Future(4)).join // Future(4)
Option(Option(3)).join // Option(3)
List(List(1, 2)).join // List(1, 2)
如果您在这种情况下将Future[Option]
其视为一种F
类型 - 您可以将它们结合在一起!您只需要证明 Future[Option] 是一个单子。ScalaZ 有一个 monad 转换器(用于“组合”或从较小的 Monad 组装更大的 Monad 的方式)。
import scalaz.OptionT.optionT
val FOT = OptionT.optionTMonadPlus[Future] //creating monad transformer on top of Option
val fo2 = FOT.join(optionT(fofo.map(_.map(optionT(_))))).run // Future(Some(5))
optionT
将 a Future[Option]
inside 放在 monad 中,所以我们需要执行 2 次 - 一次用于外部Future[Option]
,一次用于内部Future[Option]
这种方法适用于任何类型的 F[G[F[G]],你有一个 Monad Transformer。
我们也可以把它做得更好一点,避免这两个丑陋的map
内部Future
。Map 来自Functor
typeclass,它们是可组合的!所以我们可以组装我们的新的Functor[Future[Option]]
和map
在它之上:
val FOT = OptionT.optionTMonadPlus[Future]
val FOF = Functor[Future].compose[Option]
val fo2 = FOT.join(optionT(FOF.map(fofo)(optionT(_)))).run