Haskell库引发异常的背后的主意是什么

时间:2018-10-25 10:58:35

标签: haskell

为什么库(例如404上的wreq)会引发异常,而不是将结果包装到类似Maybe的东西中?

天真的,我认为Maybe会更好(例如,如果我不处理所有情况,编译器会警告我)。为什么我在这里错了?

1 个答案:

答案 0 :(得分:15)

Haskellers竭力避免从 functions 中引发异常-函数仅应在真正例外的情况下引发异常,即在“永远不发生”的情况下,例如用户传递明确输入的某些信息文档禁止。如果纯函数定期抛出异常,那么这将是一个大问题,不仅因为类型没有说明应准备捕获的内容,而且不能捕获纯函数中的异常,而仅仅是在IO代码中调用该函数。而且即使您原则上可以捕获异常,也可能很难预测在何处,因为惰性评估会延迟实际发生的时间。

事实上,即使在Wreq.get该函数不会引发任何异常

Prelude Network.Wreq> get "htt:/p/this-isn't even valid URL syntax" `seq` "Ok"
"Ok"

执行时抛出的是IO操作:

Prelude Network.Wreq> get "htt:/p/this-isn't even valid URL syntax" >> pure ()
*** Exception: InvalidUrlException "htt:/p/this-isn't%20even%20valid%20URL%20syntax" "Invalid scheme"

现在使用IO动作,情况有所不同。在不同情况下,IO操作的很多可能具有非常不同的错误,这些错误可能很难或无法预测,例如硬盘驱动器崩溃。用适合的数据类型为每个操作对所有可能的错误进行分类将是一项艰巨的任务,并且处理每种可能的情况或弄清楚哪些部分继续下去是非常麻烦的。并且仅将每个IO操作的结果包装在Maybe中将导致与Java类似的情况,其中每个引用都可能为空。这不会告诉您任何信息,而且人们通常也不会想出处理此问题的明智方法。

这几乎就是为什么首先发明例外的问题,它在程序语言中与在Haskell中一样适用(或更确切地说,它是IO的程序eDSL)。而且由于与纯函数不同,IO在其控制流中确实具有明确定义的时间顺序,因此,如果需要捕获某些特殊异常,则也很清楚必须在哪里执行。

这并不是说IO动作返回一个Maybe / Either值(使可能的错误变得很明显)就没有任何意义,但这并不总是值得的。 / p>