5

怎么变身Future[Option[Future[Option[X]]]]Future[Option[X]]

如果它是 aTraversableOnce而不是Option我会使用Future 伴侣对象;但是选项呢?

例子:

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)
      )
    )
  )
4

4 回答 4

7

更新的答案

这可能会奏效。我所做的是用最外层的 a 和最外层的模式匹配替换你的前两个map调用。flatMapFutureOption

val futOptFutOpt: Future[Option[String]] =
  futOpt.flatMap {
    case None => Future.successful(None)
    case Some(x) =>
      processAndReturnFuture(x).map {
        processedX => processAgainAndReturnOption(processedX)
      }
  }

初步答案

我假设在您的代码中某处您有一个将 a转换为 a的map调用。将其替换为 a并删除结果中的最顶层。你最终会得到. 这就是我的意思:Future[Option[A]]Future[Option[Future[Option[X]]]]mapflatMapOptionFuture[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

对于您的实际上下文,我可能并不完全正确,但是您可能会通过将几个maps替换为几个 s 来解决这个问题flatMap

于 2013-10-18T11:43:07.643 回答
4

如果您使用的是scalaZcat,您可以使用更通用的方法,这将适用于更多类型(Future需要有Traversetypeclass,并且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实例并且GMonad- 你可以这样做。更改FutureList,实现不会改变。

另一种可能很有趣的方法是使用 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 来自Functortypeclass,它们是可组合的!所以我们可以组装我们的新的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
于 2016-09-30T17:59:20.573 回答
1

以下是我对此事的看法:

val futOptFutOpt: Future[Option[String]] =
  futOpt.map(_.toSeq)
    .flatMap(Future.traverse(_)(processAndReturnFuture))
    .map(_.headOption)

这基本上是

  1. futOpt从a转换Future[Option]为aFuture[Seq]
  2. Seq处理with的每个元素processAndReturnFuture并返回一个新的Future[Seq]
  3. 转换Future[Seq]回一个Future[Option]
于 2016-08-05T13:11:16.670 回答
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
} 
于 2016-09-21T18:34:03.557 回答