Scala Cats从monad堆栈中获取值

Scala Cats从monad堆栈中获取值,scala,monads,monad-transformers,scala-cats,Scala,Monads,Monad Transformers,Scala Cats,我有一个monad堆栈,用于响应使用cats monad transformers实现的组件: type FutureEither[A] = EitherT[Future, Error, A] type FutureEitherOption[A] = OptionT[FutureEither, A] 结果是: Future[Either[Error, Option[A]]] 如何以正确的方式从堆栈中获取值或错误?如何以适当的方式组合并行执行的多个调用的结果?例如,在这种情况下: def f

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

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
def fooServiceCall:futureetheroption[Foo]
def barServiceCall(f:选项[Foo]):FutureEitherOption[Bar]
为了{

r1您的第二个方法
barServiceCall
在其签名中告诉您,它可以直接处理
Option[Foo]
,而不是依赖monad transformer堆栈在某一点上以
None
失败。因此,您必须解压缩一层
OptionT[EitherT[Future,Error,,],A]
,而是直接处理
EitherT[Future,Error,Option[A]]
:尽管您的方法似乎在前一个monad堆栈中返回结果,但这方面正确的理解工具是后一个

回想一下,如果
o:OptionT[F,A]
,则包装的
o.value
F[Option[A]]
类型

因此,如果您只需在
选项[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)

您可以在
选项[F,a]
上调用
.value
来获取
F[Option[a]]
。只需执行
选项()
做相反的操作。我相信
EitherT
有类似的方法,当您需要访问内部
选项
s或
选项
s时,您可以使用这些方法包装和打开这些类。例如,我认为您可以执行以下操作:

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

r1谢谢,看起来不错。你能澄清一件事吗?如果
fooServiceCall.value
将返回
None
,下一个调用将是
barservicecoll(None)
?我现在坚持使用它……我认为
选项[T]
可能不是一个好的选择。从一方面来说,这是可以的,因为非值不是错误,但从另一方面来说,
条形码服务应该处理它,这对我来说并不好。正如我在回答中试图勾勒出的,似乎你基本上是在
EitherT[错误,未来,,]
。在程序的某一点上,
恰好是
选项[Foo]
,但由于
barServiceCall
能够以一种有意义的方式处理
None
,因此您不会失败,而是将
None
传递给
barServiceCall
。因此,由
OptionT
建模的失败模式似乎没有必要:您可以从
None
恢复并继续计算可能这里并不真正需要
选项,因为它只会妨碍(至少对这两种方法是如此)。@theodor我可以添加一个版本,其中选择的类型构造函数稍有不同,而不是
futureitheroption
。但它会变得更“代码审查”那么,我认为你对
option
的直觉是正确的。
val x: FutureEither[Option[Bar]] = for {
  r1 <- fooServiceCall.value
  r2 <- barServiceCall(r1).value
} yield r2
val result: FutureEitherOption[Bar] = OptionT(x)