Option
monad是一种非常有表现力的方式来处理Scala中的某些东西或什么都没有。但是如果在“无”发生时需要记录消息呢?根据Scala API文档,
Either类型通常用作 替代scala.Option在哪里左 代表失败(按惯例)和 对等于某些人。
但是,我没有运气找到使用Either的最佳实践或涉及Either处理失败的真实世界示例。最后,我为自己的项目提出了以下代码:
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)
}
(请注意这是一个真实项目的片段,所以它不会自行编译)
我很高兴知道您在代码中使用Either
和/或在重构上述代码方面有更好的想法。
答案 0 :(得分:49)
要么用于返回可能的两个有意义的结果之一,不像Option用于返回单个有意义的结果,或者什么都没有。
下面给出了一个易于理解的例子(前一段时间在Scala邮件列表上发布):
def throwableToLeft[T](block: => T): Either[java.lang.Throwable, T] =
try {
Right(block)
} catch {
case ex => Left(ex)
}
正如函数名所暗示的,如果“block”的执行成功,它将返回“Right(&lt; result&gt;)”。否则,如果抛出Throwable,它将返回“Left(&lt; throwable&gt;)”。使用模式匹配来处理结果:
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
希望有所帮助。
答案 1 :(得分:13)
Scalaz库有类似之处任何一个名为Validation。它比Either更习惯于“获得有效结果或失败”。
验证还允许累积错误。
编辑:“相似”要么是错误的,因为验证是一个应用函子,而scalaz或者,名为\ /(发音为“disjonction”或“或者”),是一个monad。 验证可以确定错误的事实是因为这种性质。另一方面,/有一个“早期停止”的性质,停在它遇到的第一个 - \ /(读它“左”或“错误”)。这里有一个完美的解释:http://typelevel.org/blog/2014/02/21/error-handling.html
根据评论的要求,复制/粘贴上述链接(删除了一些行):
// 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"))
答案 2 :(得分:7)
你发布的片段看起来非常人为。您可以在以下情况下使用Either:
将异常转换为左派确实是一个常见的用例。通过try / catch,它具有将代码保持在一起的优点,如果异常是预期结果,这是有意义的。最常用的处理方式是模式匹配:
result match {
case Right(res) => ...
case Left(res) => ...
}
处理Either
的另一种有趣方式是它出现在集合中。在集合上执行映射时,抛出异常可能不可行,并且您可能希望返回除“不可能”之外的一些信息。使用Either可以在不增加算法负担的情况下执行此操作:
val list = (
library
\\ "books"
map (book =>
if (book \ "author" isEmpty)
Left(book)
else
Right((book \ "author" toList) map (_ text))
)
)
这里我们得到了库中所有作者的列表, plus 没有作者的书籍列表。因此我们可以相应地进一步处理它:
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
所以,基本的使用都是这样的。这不是一个特别有用的课程,但如果你之前已经看过它了。另一方面,它也没用。
答案 3 :(得分:0)
Cats有一个很好的方法,可以根据引发异常的代码来创建Either:
val either: Either[NumberFormatException, Int] =
Either.catchOnly[NumberFormatException]("abc".toInt)
// either: Either[NumberFormatException,Int] = Left(java.lang.NumberFormatException: For input string: "abc")
in https://typelevel.org/cats/datatypes/either.html#working-with-exception-y-code