【问题标题】:Cats get value from the monad stack猫从单子堆栈中获得价值
【发布时间】:2018-08-06 09:11:51
【问题描述】:

我有一个 monad 堆栈,用于来自我的组件的响应,这些组件是用猫 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

【问题讨论】:

    标签: scala monads monad-transformers scala-cats


    【解决方案1】:

    您的第二种方法barServiceCall 在其签名中告诉它可以直接处理Option[Foo],而不是依赖于monad 转换器堆栈在某些时候以None 失败。因此,您必须解压一层OptionT[EitherT[Future, Error, ?], A],而直接处理EitherT[Future, Error, Option[A]]:即使您的方法似乎在前一个monad堆栈中返回结果,但这个理解的正确工具是后一个。

    回想一下,如果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)
    

    【讨论】:

    • 谢谢,看起来不错。你能澄清一件事吗?如果fooServiceCall.value 将返回None,下一次调用会是barServiceCall(None)吗?我现在坚持下去......我认为Option[T] 可能不是一个好的选择。从一方面看没问题,因为非值不是错误,但从另一方面看,bar 服务应该处理它,这对我来说看起来不太好。
    • 正如我试图在我的回答中勾勒出来的那样,您似乎基本上是在EitherT[Error, Future, ?] 工作。在程序的某一时刻,? 恰好是Option[Foo],但由于barServiceCall 可以以一种有意义的方式处理None,您不会失败,而是将None 传递给barServiceCall .因此,OptionT 建模的故障模式似乎没有必要:您可以从None 恢复并继续计算。因此,OptionT 可能在这里并不真正需要,因为它只会妨碍(至少对于这两种方法而言)。
    • @theodor 我可以添加一个选择类型构造函数略有不同的版本,而不是FutureEitherOption。但它会变得更像“代码审查”风格......我认为你已经对OptionT有了正确的直觉。
    【解决方案2】:

    您可以通过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)
    

    【讨论】:

      猜你喜欢
      • 2020-01-12
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-12
      • 1970-01-01
      相关资源
      最近更新 更多