使用任一处理 Scala 代码中的故障

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1193333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:33:37  来源:igfitidea点击:

Using Either to process failures in Scala code

scalafunctional-programmingeither

提问by Alexander Azarov

Optionmonad is a great expressive way to deal with something-or-nothing things in Scala. But what if one needs to log a message when "nothing" occurs? According to the Scala API documentation,

Optionmonad 是一种很好的表达方式,可以在 Scala 中处理有或无的事情。但是,如果在“无事发生”时需要记录一条消息怎么办?根据 Scala API 文档,

The Either type is often used as an alternative to scala.Option where Left represents failure (by convention) and Right is akin to Some.

任一类型通常用作 scala.Option 的替代,其中 Left 表示失败(按照惯例),而 Right 类似于 Some。

However, I had no luck to find best practices using Either or good real-world examples involving Either for processing failures. Finally I've come up with the following code for my own project:

但是,我没有运气找到使用“要么”或涉及“要么”的真实世界示例来处理失败的最佳实践。最后,我为我自己的项目提出了以下代码:

    def logs: Array[String] = {
        def props: Option[Map[String, Any]] = configAdmin.map{ ca =>
            val config = ca.getConfiguration(PID, null)
            config.properties getOrElse immutable.Map.empty
        }
        def checkType(any: Any): Option[Array[String]] = any match {
            case a: Array[String] => Some(a)
            case _ => None
        }
        def lookup: Either[(Symbol, String), Array[String]] =
            for {val properties <- props.toRight('warning -> "ConfigurationAdmin service not bound").right
                 val logsParam <- properties.get("logs").toRight('debug -> "'logs' not defined in the configuration").right
                 val array <- checkType(logsParam).toRight('warning -> "unknown type of 'logs' confguration parameter").right}
            yield array

        lookup.fold(failure => { failure match {
            case ('warning, msg) => log(LogService.WARNING, msg)
            case ('debug, msg) =>   log(LogService.DEBUG, msg)
            case _ =>
        }; new Array[String](0) }, success => success)
    }

(Please note this is a snippet from a real project, so it will not compile on its own)

(请注意这是一个真实项目的片段,所以它不会自己编译)

I'd be grateful to know how you are using Eitherin your code and/or better ideas on refactoring the above code.

如果知道您Either在代码中的使用方式和/或有关重构上述代码的更好想法,我将不胜感激。

采纳答案by Walter Chang

Either is used to return one of possible two meaningful results, unlike Option which is used to return a single meaningful result or nothing.

与用于返回单个有意义的结果或不返回任何结果的 Option 不同,Either 用于返回可能的两个有意义的结果之一。

An easy to understand example is given below (circulated on the Scala mailing list a while back):

下面给出了一个易于理解的示例(不久前在 Scala 邮件列表中流传):

def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] =
  try {
    Right(block)
  } catch {
    case ex => Left(ex)
  }

As the function name implies, if the execution of "block" is successful, it will return "Right(<result>)". Otherwise, if a Throwable is thrown, it will return "Left(<throwable>)". Use pattern matching to process the result:

顾名思义,如果“block”执行成功,就会返回“Right(<result>)”。否则,如果抛出 Throwable,它将返回“Left(<throwable>)”。使用模式匹配来处理结果:

var s = "hello"
throwableToLeft { s.toUpperCase } match {
  case Right(s) => println(s)
  case Left(e) => e.printStackTrace
}
// prints "HELLO"

s = null
throwableToLeft { s.toUpperCase } match {
  case Right(s) => println(s)
  case Left(e) => e.printStackTrace
}
// prints NullPointerException stack trace

Hope that helps.

希望有帮助。

回答by fanf42

Scalaz library has something alike Either named Validation. It is more idiomatic than Either for use as "get either a valid result or a failure".

Scalaz 库有类似的东西要么命名为验证。它比“获得有效结果或失败”更惯用。

Validation also allows to accumulate errors.

验证还允许累积错误。

Edit: "alike" Either is complettly false, because Validation is an applicative functor, and scalaz Either, named \/ (pronounced "disjonction" or "either"), is a monad. The fact that Validation can accumalate errors is because of that nature. On the other hand, / has a "stop early" nature, stopping at the first -\/ (read it "left", or "error") it encounters. There is a perfect explanation here: http://typelevel.org/blog/2014/02/21/error-handling.html

编辑:“alike”Either 是完全错误的,因为 Validation 是一个应用函子,而名为 \/(发音为“disjonction”或“either”)的 scalazEither 是一个 monad。验证可以累积错误的事实正是由于这种性质。另一方面, / 具有“提前停止”的性质,在它遇到的第一个 -\/(读作“左”或“错误”)处停止。这里有一个完美的解释:http: //typelevel.org/blog/2014/02/21/error-handling.html

See: http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html

请参阅:http: //scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.html

As requested by the comment, copy/paste of the above link (some lines removed):

根据评论的要求,复制/粘贴上述链接(删除了一些行):

// Extracting success or failure values
val s: Validation[String, Int] = 1.success
val f: Validation[String, Int] = "error".fail

// It is recommended to use fold rather than pattern matching:
val result: String = s.fold(e => "got error: " + e, s => "got success: " + s.toString)

s match {
  case Success(a) => "success"
  case Failure(e) => "fail"
}

// Validation is a Monad, and can be used in for comprehensions.
val k1 = for {
  i <- s
  j <- s
} yield i + j
k1.toOption assert_? Some(2)

// The first failing sub-computation fails the entire computation.
val k2 = for {
  i <- f
  j <- f
} yield i + j
k2.fail.toOption assert_? Some("error")

// Validation is also an Applicative Functor, if the type of the error side of the validation is a Semigroup.
// A number of computations are tried. If the all success, a function can combine them into a Success. If any
// of them fails, the individual errors are accumulated.

// Use the NonEmptyList semigroup to accumulate errors using the Validation Applicative Functor.
val k4 = (fNel <**> fNel){ _ + _ }
k4.fail.toOption assert_? some(nel1("error", "error"))

回答by Daniel C. Sobral

The snippet you posted seems very contrived. You use Either in a situation where:

您发布的片段似乎非常做作。您在以下情况下使用Either:

  1. It's not enough to just know the data isn't available.
  2. You need to return one of two distinct types.
  1. 仅仅知道数据不可用是不够的。
  2. 您需要返回两种不同类型之一。

Turning an exception into a Left is, indeed, a common use case. Over try/catch, it has the advantage of keeping the code together, which makes sense if the exception is an expected result. The most common way of handling Either is pattern matching:

将异常变成 Left 确实是一个常见的用例。与 try/catch 相比,它具有将代码保持在一起的优点,如果异常是预期结果,这很有意义。最常用的处理方法就是模式匹配:

result match {
  case Right(res) => ...
  case Left(res) => ...
}

Another interesting way of handling Eitheris when it appears in a collection. When doing a map over a collection, throwing an exception might not be viable, and you may want to return some information other than "not possible". Using an Either enables you to do that without overburdening the algorithm:

另一种有趣的处理方式Either是当它出现在集合中时。在对集合进行映射时,抛出异常可能不可行,并且您可能希望返回“不可能”以外的一些信息。使用Either 可以让您在不增加算法负担的情况下做到这一点:

val list = (
  library 
  \ "books" 
  map (book => 
    if (book \ "author" isEmpty) 
      Left(book) 
    else 
      Right((book \ "author" toList) map (_ text))
  )
)

Here we get a list of all authors in the library, plusa list of books without an author. So we can then further process it accordingly:

在这里,我们获得了图书馆中所有作者的列表,以及没有作者的书籍列表。所以我们可以相应地进一步处理它:

val authorCount = (
  (Map[String,Int]() /: (list filter (_ isRight) map (_.right.get))) 
   ((map, author) => map + (author -> (map.getOrElse(author, 0) + 1)))
  toList
)
val problemBooks = list flatMap (_.left.toSeq) // thanks to Azarov for this variation

So, basic Either usage goes like that. It's not a particularly useful class, but if it were you'd have seen it before. On the other hand, it's not useless either.

所以,基本的任一种用法就是这样。这不是一个特别有用的类,但如果是这样,您以前就会看到它。另一方面,也不是没有用。

回答by vlfig

Cats has a nice way to create an Either from exception-throwing code:

Cats 有一个很好的方法可以从抛出异常的代码中创建一个要么:

val either: Either[NumberFormatException, Int] =
  Either.catchOnly[NumberFormatException]("abc".toInt)
// either: Either[NumberFormatException,Int] = Left(java.lang.NumberFormatException: For input string: "abc")

inhttps://typelevel.org/cats/datatypes/either.html#working-with-exception-y-code

https://typelevel.org/cats/datatypes/either.html#working-with-exception-y-code