/ / Как да премахнем много слоеве от бъдещо бъдеще в отговор - скала, playframework

Как да премахнете много слоеве Future Option Future в отговора - скала, playframework

Как мога да "поправя" този отговор, исках Бъдеще [Опция [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]]
}
}
}
}

Актуализация

Опитвам се да използвам скалаз, но получавам грешка:

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)

Отговори:

1 за отговор № 1

Това е идеалното решение котки OptionT монада трансформатор.

Имате нужда от внос на котки:

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 -> .run) само чрез промяна на вноса.

import scalaz._
import Scalaz._

като def insertF(f1: Int, f2: Int, f3: Int): Future[F4] вместо Future[Option[F4]] можете да пренапишете разбирането (използвайки 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

0 за отговор № 2
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.