为什么Option /也许被认为是一个好主意,而检查异常却不是?


23

一些编程语言(例如Scala)具有Option类型(也称为Maybe)的概念,可以包含值也可以不包含值。

根据我对它们的了解,与相比null,它们被广泛认为是处理此问题的一种更好的方法,因为它们明确地迫使程序员考虑可能没有值的情况,而不仅仅是在运行时崩溃。

另一方面,Java中的Checked Exception似乎被认为是一个坏主意,Java似乎是实现它们的唯一被广泛使用的语言。但是它们背后的想法似乎与该Option类型有些相似,以明确地迫使程序员处理可能引发异常的事实。

Option类型所没有的Checked Exception还有其他问题吗?还是这些想法与我想的不一样,并且有充分的理由强制对Option而不是对Exception进行显式处理?


另请参阅Either e a数据类型。

4
关于已检查的异常:作为使用开放源代码和内部Java库的众多用户,它们具有不断发展的代码库以及缺少/过时的文档,我为Java不会强制执行某些要明确声明的异常而感到震惊。意外的是,在恶劣的地方突然弹出未处理的运行时错误的噩梦。Java7最终使异常处理几乎变得理智,摆脱了许多旧的try-catch混乱。
海德

Answers:


24

因为Options是可组合的。有很多对有用的方法Option,让你编写简洁的代码,同时还允许对流量的精确控制:mapflatMaptoListflatten和更多。这是由于这Option是一种特殊的monad,我们很了解如何组成某些对象。如果您没有这些方法,而必须始终对模式进行匹配Option,或者isDefined经常调用它们,它们将几乎没有用处。

相反,虽然检查异常确实增加了一些安全性,但是除了捕获它们或让它们冒泡堆积(在类型声明中添加了样板)之外,您不能采取其他任何措施。


1
受检查的异常几乎以相同的方式构成... try {/* bunch of complex code involving calls to 50 different methods that may throw SomeCheckedException */} catch(SomeCheckedException e) {/* operation failed, do something */}和之间的区别fromMaybe someDefaultValue (something >>= otherThing >>= ...50 other functions that may return Nothing...)到底是什么?除了前者为您提供了发生问题的更多详细信息这一事实之外。
user253751

14

虽然相关,但是异常和Maybe对象不会处理相同类型的问题。

例外情况

当您不得不在非本地情况下处理特殊情况(在某些情况下恰巧是一个错误)时,异常确实会闪耀。例如,您正在解析一个csv,并且想要保护自己免受格式错误的行的侵害。发现错误的地方可能是一些函数调用,而不是进行行迭代。如果您在最深层(了解格式化问题)抛出异常,则可以在循环中捕获该异常,记录错误,然后继续进行下一行。您无需在其余的代码中进行任何修改。

由于所有中间函数都必须声明throwable类型,因此检查异常会增加很多麻烦。该功能无法达到最初的目的,这就是为什么它们如今不受欢迎的原因。

也许是对象

当您能够在本地处理“故障”时,也许应该选择对象。从这种意义上讲,它们是返回码+通过引用api传递或可为空的类型的替代。

Maybe对象的优点在于,您可以明确声明某些问题。在haskell中,非对象可能必须具有值,否则程序将无法编译。

可为空的类型的问题在于,必须始终检查null才是绝对安全的。默认状态为“某些地方可能出错”。

返回代码+通过ref api传递的问题是,大多数人对它们的可读性较低。


1
@MattFenwick感谢您的反馈。您为什么认为csv示例没有意义?OP并没有真正要求避免样板技术,并且我觉得诸如应用函子和monads之类的词汇对于这个问题可能太技术化了。
西蒙·贝格

1
我想指出的是,对于Java(不确定带有受检查的异常的其他语言),IDE负责添加和修剪抛出以及更新javadoc注释样板部分。因此,至少该部分没有麻烦,当然也没有痛苦。在进行API设计时,无论是痛苦,恩赐还是两者之间的麻烦,这都是另一回事……
hyde 2013年

5
@hyde:仅仅因为IDE可以自动生成无意义的样板并不意味着无意义的样板就不难了。
Michael Shaw

2
@海德:但痛苦没有消除。毫无意义的样板仍然存在,无故使代码混乱。如果有样板的原因,那是什么?
Michael Shaw 2013年

2
@MichaelShaw如果异常是没有意义的,请将其删除:忽略这种情况或返回错误值。如果是错误或无法恢复的情况:请使用未经检查的异常。剩下的与示例类型的参数一样重要,而不是无意义的样板。如果现有库中的API错误,请考虑使用其他库的包装器方法/类,或者仅使用不良API。
海德

1

因为这样Maybe可以延迟处理错误,直到您真正需要该值为止(可能需要几个方法调用)

而已检查的异常需要在呼叫位置处理

唯一的例外是可以传递有关失败原因的更多信息(除非有人MaybeError在错误发生时使用可抛出字段开发)


2
但是我可以通过声明我的方法抛出此异常来推迟对检查到的异常的处理。
疯狂的科学家

1
@MadScientist只会进入调用堆栈,而也许可以遍及所有方向
棘手的怪胎

5
我认为您不应将Maybe类型与处理错误混淆。异常用于报告错误,选项类型用于表示部分函数的结果。返回的部分函数Nothing不是错误。
Giorgio 2013年

@MadScientist:如果方法调用返回“无效值”指示,则该语句之后可以立即执行。相比之下,如果方法抛出未立即捕获的异常,则调用后的语句将被跳过。让已检查的异常渗透到整个调用堆栈中通常是邪恶的(并且应该不是处理它们的“最简单”方法),因为调用者无法分辨条件是否具有它所调用的方法所期望的含义,或者它是否表示被调用方法正在冒泡的意外情况。
超级猫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.