15

从 Scala 开始并阅读有关Either我自然地将新概念与我所知道的东西(在本例中来自 Java)进行比较的文章。与检查异常的概念有什么区别Either吗?

在这两种情况下

  • 失败的可能性在方法中明确注释(throws或返回Either
  • 程序员可以在错误发生时直接处理错误情况或将其向上移动(再次返回Either
  • 有一种方法可以通知调用者错误的原因

我想有人使用 for-comprehensions onEither来编写代码,因为不会出现类似于检查异常的错误。

我想知道我是否是唯一一个在看到差异时遇到问题的初学者。

谢谢

4

3 回答 3

13

Either不仅可以用于异常。例如,如果您要让用户为您键入输入或指定包含该输入的文件,您可以将其表示为Either[String, File].

Either经常用于异常处理。Either和受检异常的主要区别在于控制流Either总是显式的。编译器真的不会让你忘记你正在处理一个Either; 它不会在您不知道的情况下从多个地方收集Eithers,返回的所有内容都必须是Either,等等。因此,您Either不要在可能出现异常情况时使用,而是作为控制程序的正常部分执行。此外,Either不捕获堆栈跟踪,使其比典型异常更有效。

另一个区别是异常可用于控制流。需要跳出三个嵌套循环?没问题——抛出异常(没有堆栈跟踪)并在外部捕获它。需要跳出五个嵌套的方法调用?没问题!要么不提供这样的东西。

也就是说,正如您所指出的,有许多相似之处。你可以传回信息(虽然Either这很简单,而检查异常让你编写自己的类来存储你想要的任何额外信息);你可以把它传递Either下去,或者你可以把它折叠成别的东西,等等。

因此,总而言之:尽管您可以在Either显式错误处理方面使用检查异常来完成相同的事情,但它们在实践中是相对不同的。特别是,Either使创建和传回不同的状态变得非常容易,而受检异常擅长绕过所有正常的控制流,希望返回到可以明智地处理异常情况的地方。

于 2012-05-30T14:54:35.017 回答
2

就返回签名形成排他性析取而言,要么等效于已检查异常。结果可以是抛出的异常 X 或 A。但是,抛出异常并不等同于返回一个异常——第一个异常不是引用透明的。

Scala 的 Either 不是(从 2.9 开始)等价的地方是返回类型是正偏的,并且需要努力提取/解构异常, Either 是无偏的;您需要明确要求左值或右值。这是一些讨论的话题,在实践中有点痛苦——考虑以下三个对 Either 生产方法的调用

for {
  a <- eitherA("input").right
  b <- eitherB(a).right
  c <- eitherC(b).right
} yield c // Either[Exception, C]

您需要手动穿过 RHS。这可能看起来不那么繁重,但实际上对于新手来说是一种痛苦并且有些令人惊讶。

于 2012-05-31T02:38:15.493 回答
2

是的,Either是一种在语言中嵌入异常的方法;其中一组可能失败的操作可能会向某些非本地站点抛出错误值。

除了 Rex 提到的实际问题之外,您还可以从 a 的简单语义中获得一些额外的东西Either

  • Either形成一个单子;因此您可以对计算结果为 的表达式集使用一元操作Either。例如,无需测试结果即可进行短路评估
  • Either在类型中——所以类型检查器本身就足以跟踪对值的错误处理

一旦您能够返回错误消息(Left s)或成功的值Right v,您就可以将异常分层,就像在 Haskell中所做Either那样加上错误处理程序。MonadError

于 2012-05-30T16:28:46.633 回答