有没有一种优雅的方式来处理 Scala 中的 Either Monad?
Is there a elegant way to handle Either Monad in Scala?
我开始使用 Scala,用 circe 做一个项目来处理 JSON。
我从函数中遇到了很多 Either returns,但我似乎没有找到一种优雅的方式来处理所有这些。
例如,对于一个单一的,我在这个片段中做:
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
)
当我想获取它们的内容时,should/can 如何处理多个 Either
对象(不会以一堆 if-elses 或类似的结尾)Right
,但不是 我不确定它们是否总是 Right
?
假设您有一个案例 class,
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
是连续的,这意味着如果第一个 Either
是一个 Left
那么你甚至不会看第二个 Either
并且只是得到一个Left
。这对于验证来说是不可取的,因为您不想丢失所有组件的验证信息,所以您真正想要的是 Applicative
.
并且 Either
不是 Applicative
,您必须使用 cats
或 scalaz
或为此实现您自己的应用程序。
cats 为此目的提供了 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))
}
How should/can I handle multiple Either
objects (without ending up with a bunch of if-elses, or similar) when I want to get their contents if they are a Right
, but not I am not sure they are always a Right
?
所以你有一些 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
,但我认为这就是您所要求的。
我开始使用 Scala,用 circe 做一个项目来处理 JSON。
我从函数中遇到了很多 Either returns,但我似乎没有找到一种优雅的方式来处理所有这些。
例如,对于一个单一的,我在这个片段中做:
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
)
当我想获取它们的内容时,should/can 如何处理多个 Either
对象(不会以一堆 if-elses 或类似的结尾)Right
,但不是 我不确定它们是否总是 Right
?
假设您有一个案例 class,
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
是连续的,这意味着如果第一个 Either
是一个 Left
那么你甚至不会看第二个 Either
并且只是得到一个Left
。这对于验证来说是不可取的,因为您不想丢失所有组件的验证信息,所以您真正想要的是 Applicative
.
并且 Either
不是 Applicative
,您必须使用 cats
或 scalaz
或为此实现您自己的应用程序。
cats 为此目的提供了 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))
}
How should/can I handle multiple
Either
objects (without ending up with a bunch of if-elses, or similar) when I want to get their contents if they are aRight
, but not I am not sure they are always aRight
?
所以你有一些 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
,但我认为这就是您所要求的。