简体   繁体   English

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

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

I am starting up on Scala, doing a project with circe to handle JSON.我开始使用 Scala,用 circe 做一个项目来处理 JSON。

I am coming accross a lot of Either returns from functions, and I don't seem to find a elegant way to handle all of them.我遇到了很多函数的返回值,而且我似乎没有找到一种优雅的方法来处理所有这些返回值。

For instance, for a single either, I do as in this snippet:例如,对于单个,我在此代码段中执行以下操作:

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

But what should I do when I have a lot of them in sequence, such as this example in which I just go straight for the right side and I feel I should be doing some extra validation:但是当我有很多顺序时我该怎么办,比如这个例子,我直接走到右边,我觉得我应该做一些额外的验证:

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

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

Lets say you have a case class,假设你有一个案例类,

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

and two eithers,和两个,

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

So... lets start with the most basic and obvious one,所以......让我们从最基本和最明显的开始,

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

Another way is to do the same as above is to use for-comprehensions,另一种方法是使用 for-comprehensions,

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

But, monads are sequential, which means that if the first Either is a Left then you will not even look at the second Either and just get a Left .但是, monads是连续的,这意味着如果第一个EitherLeft那么您甚至不会查看第二个Either并且只得到一个Left This is mostly undesirable for validations because you don't want to loose the validation information of all components, so what you actually want is an Applicative .这对于验证来说通常是不可取的,因为您不想丢失所有组件的验证信息,所以您真正想要的是Applicative

And Either is not an Applicative , you will have to use cats or scalaz or implement your own applicative for this.并且Either不是Applicative ,您将不得不使用catsscalazscalaz实现您自己的应用程序。

cats provides the Validated applicative for this express purpose which lets you validate and keep all error information of the validated components.猫为此明确目的提供了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)

Or, if you are doing this just once and don't want to depend on cats , you can just use pattern-matching which is very versatile或者,如果您只这样做一次并且不想依赖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 ?当我想获取它们的内容(如果它们是Right ,但不是我不确定它们总是Right )时,我应该/如何处理多个Either对象(而不会以一堆 if-else 或类似的方式结束)?

So you have some Either instances, all with the same type signature.所以你有一些Either实例,它们都具有相同的类型签名。

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

And you want all the Right values, ignoring any Left values.并且您需要所有Right值,而忽略任何Left值。

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

You don't know which Either contains which String but I think that's what you asked for.您不知道哪Either包含哪个String但我认为这就是您所要求的。

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

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