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

两种类型通常用作scala的替代方案。选项,其中左代表失败(按照惯例),右表示类似于某些。

然而,我没有运气找到使用 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 在您的代码和/或重构上述代码的更好想法中。

有帮助吗?

解决方案

要么用于返回的可能的两个有意义的结果之一,不像用于返回单个有意义的结果或没有选择。

这是容易理解的例子如下(在循环的Scala邮件列表而回):

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

作为功能名称所暗示的,如果“块”的执行是成功的,它会返回“右(<结果>)”。否则,如果一个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

希望有所帮助。

其他提示

Scalaz库有什么都不管命名验证。此产品比更惯用用作“获得一个有效的结果或失败”。

验证还允许累积误差。

编辑:“相似”要么是complettly假的,因为验证是一个适用函子,和scalaz要么,名为\ /(发音为“disjonction”或“任一”),是一个单子。 该验证可以accumalate错误的情况是因为自然的。在另一方面,/有一个“提前停止”的性质,在第一次停 - \ /(读为“左”或“错误”),它遇到。有一个完美的解释一下: 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. 您需要返回两种不同类型之一。

将异常转变为左确实是一个常见的用例。与 try/catch 相比,它的优点是可以将代码保持在一起,如果异常是这样的话,这是有意义的 预期结果. 。处理 Either 的最常见方法是模式匹配:

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))
  )
)

在这里我们得到了图书馆中所有作者的列表, 没有作者的书籍清单。那么我们就可以对其进行进一步的处理:

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

所以,基本的 Either 用法就是这样的。这不是一个特别有用的类,但如果是的话,您以前应该已经见过它了。另一方面,它也并非毫无用处。

猫有一个很好的方法来创建一个无论是从异常投掷代码:

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top