使用或者在Scala代码中处理失败

Option monad是一个很好的expression方式来处理Scala中的无所事事。 但是如果在“什么都没有”的情况下需要logging一条消息呢? 根据Scala API文档,

这两种types经常用作scala的替代选项。其中,Left表示失败(按惯例),Right与Some类似。

然而,我没有运气find使用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和/或更好的想法来重构上面的代码。

或者用于返回可能的两个有意义的结果中的一个,与用于返回单个有意义的结果的Option不同。

下面给出一个简单易懂的例子(在Scala邮件列表中发送):

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

如函数名所示,如果“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 

希望有所帮助。

斯卡拉斯图书馆有一些名为validation。 这比“无论是得到一个有效的结果还是失败”都更为习惯。

validation也允许累积错误。

编辑:“相似”要么是完全错误的,因为validation是一个应用函数,并且名为\ /(发音为“disjonction”或“either”)是monad。 validation可以纠正错误的事实是因为这种性质。 另一方面,/有一个“停止早期”的性质,停在第一个\ /(读它“左”或“错误”)遇到。 这里有一个完美的解释: http : //typelevel.org/blog/2014/02/21/error-handling.html

请参阅: http : //scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/example/ExampleValidation.scala.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")) 

您发布的代码段似乎非常有人气。 您在以下情况下使用Either:

  1. 仅仅知道数据不可用是不够的。
  2. 您需要返回两种不同types之一。

把一个例外变成一个Left实际上是一个常见的用例。 在try / catch上,它具有将代码保存在一起的优点,如果exception是预期的结果 ,这是有意义的。 最常见的处理方式是模式匹配:

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

另一个有趣的处理方式是当它出现在一个集合中。 在集合上执行映射时,抛出exception可能不可行,并且您可能想要返回除“不可能”之外的一些信息。 使用这两种方法可以使您不会使algorithm负担过重:

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

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

 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 

所以,基本不pipe用法如何。 这不是一个特别有用的课程,但如果是这样的话,你以前就会看到它。 另一方面,这也不是无用的。