猫从 monad 堆栈中获取价值

Cats get value from the monad stack

我有一个 monad 堆栈,用于使用 cats monads 转换器实现的组件的响应:

type FutureEither[A] = EitherT[Future, Error, A] 
type FutureEitherOption[A] = OptionT[FutureEither, A]

结果有效:

Future[Either[Error, Option[A]]]

如何以正确的方式从此堆栈中获取值或错误?如何以正确的方式组合多个并行执行的调用的结果?例如在这种情况下:

def fooServiceCall: FutureEitherOption[Foo]
def barServiceCall(f: Option[Foo]): FutureEitherOption[Bar]

for {
  r1 <- fooServiceCall
  r2 <- barServiceCall(r1)
} yield r2

你的第二个方法 barServiceCall 在它的签名中告诉它它可以直接处理 Option[Foo],而不是依赖 monad 转换器堆栈在某些时候因 None 而失败。因此,你必须解压一层 OptionT[EitherT[Future, Error, ?], A],而不是直接处理 EitherT[Future, Error, Option[A]]:即使你的方法看起来 return 导致前一个 monad 堆栈,这个 EitherT[Future, Error, Option[A]] 中的正确工具=35=]是后一个。

回想一下,如果 o: OptionT[F, A],则包装的 o.valueF[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)

您可以在 OptionT[F, A] 上调用 .value 以获得 F[Option[A]]。只需执行 OptionT() 即可执行相反的操作。我相信 EitherT 有类似的方法,当您需要访问内部 Options 或 Eithers 时,您可以使用这些方法来包装和解开这些 类。例如,我认为您可以这样做:

val x: FutureEither[Option[Bar]] = for {
  r1 <- fooServiceCall.value
  r2 <- barServiceCall(r1).value
} yield r2
val result: FutureEitherOption[Bar] = OptionT(x)