如何将Future[Option[Future[Option[X]]]]
转换为Future[Option[X]]
?
如果是TraversableOnce
而不是Option
,我会使用Future companion object;但是选项呢?
示例:
def processAndReturnFuture(x:String):Future[String] = future(x)
def processAgainAndReturnOption(x:String):Option[String] = Some(x)
val futOpt:Future[Option[String]] = future(Some("x"))
val futOptFutOpt:Future[Option[Future[Option[String]]]] =
futOpt.map( opt =>
opt.map( x =>
processAndReturnFuture(x).map( processedX =>
processAgainAndReturnOption(processedX)
)
)
)
答案 0 :(得分:7)
这可能会成功。我所做的是用最外面的map
上的flatMap
和最外面的Future
上的模式匹配替换前两个Option
来电。
val futOptFutOpt: Future[Option[String]] =
futOpt.flatMap {
case None => Future.successful(None)
case Some(x) =>
processAndReturnFuture(x).map {
processedX => processAgainAndReturnOption(processedX)
}
}
我假设您的代码中某处有一个map
调用,可以将Future[Option[A]]
转换为Future[Option[Future[Option[X]]]]
。将map
替换为flatMap
,并删除结果中最顶层的Option
图层。你最终会得到Future[Option[X]]
。这就是我的意思:
scala> import scala.concurrent._
import scala.concurrent._
scala> import ExecutionContext.Implicits.global
import ExecutionContext.Implicits.global
scala> val f1: Future[Option[Future[Option[String]]]] =
| Future.successful(Some(1)).map(v => Some(Future.successful(Some(v.toString))))
f1: scala.concurrent.Future[Option[scala.concurrent.Future[Option[String]]]] = scala.concurrent.impl.Promise$DefaultPromise@6f900132
scala> val f2: Future[Option[String]] =
| Future.successful(Some(1)).flatMap(v => Future.successful(Some(v.toString)))
f2: scala.concurrent.Future[Option[String]] = scala.concurrent.impl.Promise$DefaultPromise@2fac9a62
关于你的实际情境,我可能并不完全正确,但你可能会用一些map
替换一些flatMap
来解决这个问题。
答案 1 :(得分:4)
如果您使用的是scalaZ或cats,则可以使用更通用的方法,这种方法适用于更多类型(Future
需要Traverse
类型类,以及Option
只需要成为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))
在这里,你首先"交换"使用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]是一个monad。
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
将Future[Option]
放入monad中,因此我们需要执行2次 - 一次用于外部Future[Option]
,另一次用于内部Future[Option]
这种方法适用于任何类型的F [G [F [G]],你有一个Monad变压器。
我们也可以让它更好一点,并避免在map
内这两个丑陋的Future
。地图来自Functor
类型类,它们是可组合的!因此,我们可以将新的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
答案 2 :(得分:1)
以下是我对此事的看法:
val futOptFutOpt: Future[Option[String]] =
futOpt.map(_.toSeq)
.flatMap(Future.traverse(_)(processAndReturnFuture))
.map(_.headOption)
这基本上是做什么的
futOpt
从Future[Option]
转换为Future[Seq]
Seq
处理processAndReturnFuture
的每个元素并返回新的Future[Seq]
Future[Seq]
转换回Future[Option]
答案 3 :(得分:1)
不完全是答案而是建议:尝试避免递归编写,而是使用for-yield
def someFunc(fo1: Future[Option[String]], fo2: Future[Option[String]]): Future[Option[String]] ={
//this is what we want to avoid
//val fofo1:Future[Option[Future[Option[String]]]] = fo1.map(o => o.map(s => fo2))
//instead use for
val res : Future[Option[String]] = for {
o1 <- fo1
o2 <- fo2
}yield{
println(o1 + "do what ever you want" + o2)
//or use for a second time
for{
s1 <- o1
s2 <- o2
}yield{
s"$o1, $o2"
}
}
res
}