您的第二种方法barServiceCall 在其签名中告诉它可以直接处理Option[Foo],而不是依赖于monad 转换器堆栈在某些时候以None 失败。因此,您必须解压一层OptionT[EitherT[Future, Error, ?], A],而直接处理EitherT[Future, Error, Option[A]]:即使您的方法似乎在前一个monad堆栈中返回结果,但这个理解的正确工具是后一个。
回想一下,如果o: OptionT[F, A],那么包装的o.value 是F[Option[A]] 类型。
因此,如果您只是在OptionT[EitherT[Future, Error, ?], A] 上调用.value,您将获得所需的EitherT[Future, Error, Option[A]]。以下是它在代码中的工作方式:
import scala.concurrent.Future
import scala.util.Either
import cats.instances.future._
import cats.instances.either._
import cats.instances.option._
import cats.data.EitherT
import cats.data.OptionT
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
type Error = String // or whatever...
type Foo = (Int, Int) // whatever...
type Bar = (String, Float) // whatever...
type FutureEither[A] = EitherT[Future, Error, A]
type FutureEitherOption[A] = OptionT[FutureEither, A]
def fooServiceCall: FutureEitherOption[Foo] = ???
def barServiceCall(f: Option[Foo]): FutureEitherOption[Bar] = ???
val resFut: Future[Either[Error, Option[Bar]]] = (for {
r1 <- fooServiceCall.value // : EitherT[Future, Error, Option[Foo]]
r2 <- barServiceCall(r1).value // : EitherT[Future, Error, Option[Bar]]
} yield r2).value
val res: Either[Error, Option[Bar]] = Await.result(resFut, 10.seconds)
现在结果很简单,您可以直接处理它。
或者,如果您现在不想解压结果,可以将其再次包装到 OptionT 中并继续使用 FutureEitherOption[Bar]:
val res2: FutureEitherOption[Bar] =
OptionT(for {
r1 <- fooServiceCall.value
r2 <- barServiceCall(r1).value
} yield r2)