fuz*_*fuz 27 io monads haskell exception-handling
任何人都可以解释为什么异常可能会被抛出IO monad,但可能只会被捕获到它内部?
Rom*_*aka 24
(纯)Haskell函数的一个简洁属性是它们的单调性 - 更明确的参数产生更多定义的值.这个属性非常重要,例如推理递归函数(阅读文章以了解原因).
根据定义,异常的表示是底部,_|_
对应于给定类型的poset中的最小元素.因此,为了满足单调性要求,以下不等式需要适用于任何f
Haskell函数的表示:
f(_|_) <= f(X)
Run Code Online (Sandbox Code Playgroud)
现在,如果我们可以捕获异常,我们可以通过"识别"底部(捕获异常)并返回更多定义的值来打破这种不等式:
f x = case catch (seq x True) (\exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
Run Code Online (Sandbox Code Playgroud)
这是完整的工作演示(需要base-4 Control.Exception):
import Prelude hiding (catch)
import System.IO.Unsafe (unsafePerformIO)
import qualified Control.Exception as E
catch :: a -> (E.SomeException -> a) -> a
catch x h = unsafePerformIO $ E.catch (return $! x) (return . h)
f x = case catch (seq x True) (\exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
Run Code Online (Sandbox Code Playgroud)
正如TomMD指出的那样,另一个原因是打破了参考透明度.你可以用相同的东西取代平等的东西,得到另一个答案 (在指称意义上相同,即它们表示相同的值,而不是==
意义上的.)
我们怎么做?请考虑以下表达式:
let x = x in x
Run Code Online (Sandbox Code Playgroud)
这是一个非终止递归,因此它永远不会返回任何信息,因此也表示_|_
.如果我们能够捕获异常,我们可以编写函数f,如
f undefined = 0
f (let x = x in x) = _|_
Run Code Online (Sandbox Code Playgroud)
(对于严格的函数,后者总是如此,因为Haskell没有提供检测非终止计算的方法 - 原则上不能,因为Halting问题.)
Tho*_*son 14
因为异常会破坏参照透明度.
您可能正在讨论实际上直接输入结果的异常.例如:
head [] = error "oh no!" -- this type of exception
head (x:xs) = x
Run Code Online (Sandbox Code Playgroud)
如果你感叹不能够捕获错误这样的话,我断言你的职能不应该依赖error
或任何其他异常,但应该使用正确的返回类型(Maybe
,Either
,或者MonadError).这迫使您以更明确的方式处理异常情况.
与上述不同(以及导致问题背后的问题),异常可以来自诸如内存条件完全独立于计算值的信号.这显然不是一个纯粹的概念,必须存在于IO中.
归档时间: |
|
查看次数: |
2030 次 |
最近记录: |