throw和throwIO

时间:2019-01-01 23:23:20

标签: haskell exception

The documentation说:

  

throwIO变量应优先于throw引发,以在IO monad中引发异常,因为它可以保证相对于其他IO操作的排序,而throw则不能。

看完之后我还是很困惑。有没有一个例子表明throw会引起问题,而throwIO不会引起问题?

其他问题:

以下陈述正确吗?

  1. 如果使用throw在IO中引发异常,则不能保证异常的顺序。
  2. 如果使用throw以非IO值引发异常,则可以保证异常的顺序。

如果我需要在Monad Transformer中抛出异常,而我必须使用throw而不是throwIO,那么它可以保证异常的顺序吗?

2 个答案:

答案 0 :(得分:4)

我认为文档可以改进。 throw等需要记住的问题是,throw返回的底值在被评估时会“爆炸”(引发异常)。但是由于懒惰,很难控制评估的时间和时间。

例如:

Prelude Control.Exception> let f n = if odd n then throw Underflow else True
Prelude Control.Exception> snd (f 1, putStrLn "this is fine")
this is fine

这可以说是您想要发生的事情,但通常不是。例如而不是上面的元组,您可能最终会得到一个带有单个爆炸元素的大数据结构,该结构会导致您的Web服务器向用户返回200或其他之后引发异常。

throwIO允许您顺序引发异常,就像它是另一个IO操作一样,因此可以对其进行严格控制:

Prelude Control.Exception> throwIO Underflow >> putStrLn "this is fine"
*** Exception: arithmetic underflow

...就像做print 1 >> print 2一样。

但是请注意,您实际上可以将throwIO替换为throw ,例如:

Prelude Control.Exception> throw Underflow >> putStrLn "this is fine"
*** Exception: arithmetic underflow

由于现在爆炸值的类型为IO a。除了记录成语之外,我实际上还不清楚为什么throwIO存在。也许其他人可以回答。

作为最后一个示例,这与我的第一个示例存在相同的问题:

Prelude Control.Exception> return (throw Underflow) >> putStrLn "this is fine"
this is fine

答案 1 :(得分:1)

throwundefined的概括,而throwIO是实际的IO动作。一个关键的区别是,在考虑严格性时(即当您拥有undefined(或throwseq)时,许多法律并不完全适用。

> (throw Underflow :: IO ()) `seq` ()
*** Exception: arithmetic underflow
> (throw Underflow >>= pure) `seq` ()
()

因此违反法律m >>= pure = mthrowIO没有这个问题,因此它是引发异常的更原则的方法。