简体   繁体   English

猫从单子堆栈中获得价值

[英]Cats get value from the monad stack

I have a monad stack for responses from my components implemented with the cats monads transformers: 我有一个monad堆栈,用于响应使用cats monads变压器实现的组件的响应:

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

the result is effectively: 结果是有效的:

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

How can I get a value or error from this stack in a proper way? 如何以适当的方式从此堆栈中获取值或错误? How can I combine the results from several calls execute in parallel in a proper way? 如何合并以适当方式并行执行的多个调用的结果? For example in such scenario: 例如在这种情况下:

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

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

Your second method barServiceCall tells in its signature that it can deal with Option[Foo] directly, instead of relying on the monad transformer stack to fail with None at some point. 您的第二个方法barServiceCall在其签名中告诉它可以直接处理Option[Foo] ,而不是依赖monad转换器堆栈在某些时候失败并显示None Therefore, you have to unpack one layer of OptionT[EitherT[Future, Error, ?], A] , and instead deal with EitherT[Future, Error, Option[A]] directly: even though your methods seem to return results in the former monad stack, the right tool in this for-comprehension is the latter one. 因此,您必须拆开一层OptionT[EitherT[Future, Error, ?], A] EitherT[Future, Error, Option[A]] ,然后直接处理EitherT[Future, Error, Option[A]] :即使您的方法似乎在前者是monad堆栈,这种理解的正确工具是后者。

Recall that if o: OptionT[F, A] , then the wrapped o.value is of type F[Option[A]] . 回想一下,如果o: OptionT[F, A] ,则包装的o.valueF[Option[A]]

Therefore, if you simply call .value on a OptionT[EitherT[Future, Error, ?], A] , you get the required EitherT[Future, Error, Option[A]] . 因此,如果仅在OptionT[EitherT[Future, Error, ?], A]上调用.valueOptionT[EitherT[Future, Error, ?], A]获得所需的EitherT[Future, Error, Option[A]] Here is how it works out in code: 这是它在代码中的工作方式:

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)

Now the result is simple enough that you can deal with it direcly. 现在,结果非常简单,您可以直接对其进行处理。

Alternatively, if you don't want to unpack the result here and now, you can wrap it again into an OptionT and keep working with a FutureEitherOption[Bar] : 另外,如果您不想在此时此地解压缩结果,可以将其再次包装到OptionT并继续使用FutureEitherOption[Bar]

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

You can call .value on a OptionT[F, A] to get a F[Option[A]] . 您可以在OptionT[F, A]上调用.value以获得F[Option[A]] And just do OptionT() to do the reverse. 只需执行OptionT()即可完成相反操作。 I believe EitherT has similar methods that you can use to wrap and unwrap these classes when you need access to internal Option s or Either s. 我相信EitherT有类似的方法,当您需要访问内部OptionEither时,可以使用它们包装和拆开这些类。 For example, I think you could do something like: 例如,我认为您可以执行以下操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM