繁体   English   中英

有没有一种优雅的方式来处理 Scala 中的任一个 Monad?

[英]Is there a elegant way to handle Either Monad in Scala?

我开始使用 Scala,用 circe 做一个项目来处理 JSON。

我遇到了很多函数的返回值,而且我似乎没有找到一种优雅的方法来处理所有这些返回值。

例如,对于单个,我在此代码段中执行以下操作:

if (responseJson.isRight) {
//do something
} else {
//do something else
}

但是当我有很多顺序时我该怎么办,比如这个例子,我直接走到右边,我觉得我应该做一些额外的验证:

ClassA(
       someValue,
       someValue,
       someJson.hcursor.get[Double]("jsonKey1").right.get,
       someJson.hcursor.get[Double]("jsonKey2").right.get,
       someJson.hcursor.get[Double]("jsonKey3").right.get
      )

当我想获取它们的内容(如果它们是Right ,但不是我不确定它们总是Right )时,我应该/如何处理多个Either对象(而不会以一堆 if-else 或类似的方式结束)?

假设你有一个案例类,

case class Demo(i: Int, s: String)

和两个,

val intEither: Either[Throwable, Int] = ???
val stringEither: Either[Throwable, Int] = ???

所以......让我们从最基本和最明显的开始,

val demoEither: Either[Throwable, Demo] = 
  intEither.flatMap(i => 
    stringEither.map(s => Demo(i, s))
  )

另一种方法是使用 for-comprehensions,

val demoEither: Either[Throwable, Demo] = 
  for {
    i <- intEither 
    s <- stringEither
  } yield Demo(i, s)

但是, monads是连续的,这意味着如果第一个EitherLeft那么您甚至不会查看第二个Either并且只得到一个Left 这对于验证来说通常是不可取的,因为您不想丢失所有组件的验证信息,所以您真正想要的是Applicative

并且Either不是Applicative ,您将不得不使用catsscalazscalaz实现您自己的应用程序。

猫为此明确目的提供了Validated应用程序,它允许您验证并保留已验证组件的所有错误信息。

import cats.data._
import cats.implicits._

val intValidated: ValidatedNec[Throwable, Int] = 
  intEither.toValidatedNec

val stringValidated: ValidatedNec[Throwable, String] =
  stringEither.toValidatedNec

val demoValidated: ValidatedNec[Throwable, Demo] = 
  (intValidated, stringValidated).mapN(Demo)

val demoEither: Either[List[Throwable], Demo] = 
  demoValidated.leftMap(errorNec => errorNec.toList)

或者,如果您只这样做一次并且不想依赖cats ,您可以使用非常通用的模式匹配

val demoEither: Either[List[Throwable], Demo] = 
  (intEither, stringEither) match {
    case (Right(i), Right(s)) => Right(Demo(i, s))
    case (Left(ti), Left(ts)) => Left(List(ti, ts))
    case (Left(ti), _) => Left(List(ti))
    case (_, Left(ts)) => Left(List(ts))
  }

当我想获取它们的内容(如果它们是Right ,但不是我不确定它们总是Right )时,我应该/如何处理多个Either对象(而不会以一堆 if-else 或类似的方式结束)?

所以你有一些Either实例,它们都具有相同的类型签名。

val ea :Either[Throwable,String] = Right("good")
val eb :Either[Throwable,String] = Left(new Error("bad"))
val ec :Either[Throwable,String] = Right("enough")

并且您需要所有Right值,而忽略任何Left值。

List(ea, eb, ec).collect{case Right(x) => x}
//res0: List[String] = List(good, enough)

您不知道哪Either包含哪个String但我认为这就是您所要求的。

暂无
暂无

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

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