如何在响应中删除Future Options Future的多个层

时间:2017-03-09 13:27:01

标签: scala playframework

我怎样才能修复"这个回复,我想要Future [Option [F4]]

val f4Response: Future[Option[Future[Option[Future[F4]]]]] = 
    for {
        f1Opt <- api.getF1() // Future[Option[F1]]
        f2Opt <- if (f1Opt.isDefined) api.getF2(f1Opt.get.id) else Future.successful(None) // getF2 is Future[Option[F3]]
    } yield {
        for {
            f1 <- f1Opt
            f2 <- f2Opt
        } yield {
            for {
                f3Opt <- api.getF3(f1.id, f2.id) // Future[Option[F3]]
            } yield {
                for {
                    f3 <- f3Opt
                } yield {
                    api.insertF(f1, f2, f3) // Future[Option[F4]]
                }
            }
        }
    }

更新

我试图使用scalaz但是我收到了错误:

val result: Future[Option[f4]] = (
            for {
              f1 <- OptionT(api.getF1(..))
              f2 <- OptionT(api.getF2(..))
              f3 <- OptionT(api.getF3(f1.id, f2.id)
            } yield api.getF4(f1, f2, f3)
          ).run

错误是:

[error]  found   : scala.concurrent.Future[Option[scala.concurrent.Future[F4]]]
[error]  required: scala.concurrent.Future[Option[F4]]

另外,我无法访问 f1.id和f2.id

f3 <- OptionT(api.getF3(f1.id, f2.id)

2 个答案:

答案 0 :(得分:1)

这是cats OptionT monad变换器的最佳选择。

你需要一些猫进口:

import cats.data.OptionT
import cats.instances.future._

让我们说这是你的数据结构(模拟):

case class F1(id: Int)
case class F2(id: Int)
case class F3(id: Int)
trait F4

object api {
  def getF1(): Future[Option[F1]] = ???
  def getF2(f1: Int): Future[Option[F2]] = ???
  def getF3(f1: Int, f2: Int): Future[Option[F3]] = ???
  def insertF(f1: Int, f2: Int, f3: Int): Future[Option[F4]] = ???
}

然后你可以这样做:

val resultT: OptionT[Future, F4] = for {
  f1 <- OptionT(api.getF1())
  f2 <- OptionT(api.getF2(f1.id))
  f3 <- OptionT(api.getF3(f1.id, f2.id))
  f4 <- OptionT(api.insertF(f1.id, f2.id, f3.id))
} yield f4

val result: Future[Option[F4]] = resultT.value

或者,您可以使用OptionT

直接包装方法
type FutOpt[T] = OptionT[Future, T]

def getF1(): FutOpt[F1] = OptionT { ??? }
def getF2(f1: Int): FutOpt[F2] = OptionT { ??? }
def getF3(f1: Int, f2: Int): FutOpt[F3] = OptionT { ??? }
def insertF(f1: Int, f2: Int, f3: Int): FutOpt[F4] = OptionT { ??? }

val resultT: FutOpt[F4] = for {
  f1 <- api.getF1()
  f2 <- api.getF2(f1.id)
  f3 <- api.getF3(f1.id, f2.id)
  f4 <- api.insertF(f1.id, f2.id, f3.id)
} yield f4

val result: Future[Option[F4]] = resultT.value

您也可以使用scalaz OptionT仅通过更改导入来保持完全相同的语法(.value - &gt; .run除外)。

import scalaz._
import Scalaz._

使用def insertF(f1: Int, f2: Int, f3: Int): Future[F4]代替Future[Option[F4]],您可以将for-comprehension(使用scalaz)重写为:

val resultT: OptionT[Future, F4] = for {
  f1 <- OptionT(api.getF1())
  f2 <- OptionT(api.getF2(f1.id))
  f3 <- OptionT(api.getF3(f1.id, f2.id))
  f4 <- api.insertF(f1.id, f2.id, f3.id).liftM[OptionT]
} yield f4

val result: Future[Option[F4]] = resultT.run

答案 1 :(得分:0)

val f4Response: Future[Option[Int]] = api.getF1() flatMap {
  case Some(f1) => {
    api.getF2(f1).flatMap {
      case Some(f2) => {
        api.getF3(f1.id, f2.id).flatMap {
          case Some(f3) => bar(f1, f2, f3)
        }
      }
    }
  }
}

for yield对于这种情况可能没有必要patter match可能更好,直接没有Option.get(可能会失败),{pattern match更安全IUserSessionService {1}}。