如何在Haskell中正确使用Control.Exception.catch? [英] How do I correctly use Control.Exception.catch in Haskell?
问题描述
catch(return $ head [ ])$ \(e :: SomeException) - >返回好消息
返回
***例外:Prelude.head:空列表
但是
catch(print $ head [])$ \(e :: SomeException) - > printgood message
退货
好消息
为什么不是第一个案例捕捉异常?他们有什么不同?为什么第一个案例会在异常信息前加上一个双引号?
谢谢。
让我们来看看在第一种情况下会发生什么:
catch(return $ head [] )$ \(e :: SomeException) - > returngood message
头部[]
即返回
编辑为 IO
动作。这个thunk不会抛出任何异常,因为它没有被评估,所以w调用 catch(return $ head [])$ ...
(它是 IO字符串
类型)产生字符串
thunk没有异常。只有当ghci尝试打印结果后才会发生异常。如果您尝试过,则可以使用
$ p $ catch(return $ head [])$ \(e :: SomeException) - >返回好消息
>>返回()
相反,不会打印出异常。
这也是你为什么得到_ * 例外的原因:Prelude.head:空list_。GHCi开始打印字符串,以开头。然后尝试评估字符串,这会导致出现异常,并将其打印出来。
尝试替换 return
with evaluate
(它强制它的参数为WHNF)作为
catch (评估$ head [])$ \(e :: SomeException) - >返回好消息
然后您将强制thunk在 catch
这会抛出异常并让处理程序拦截它。
在另一种情况下
catch(print $ head [])$ \(e :: SomeException) - >打印好消息
在 catch $ c当$
print
试图检查 head []
时,$ c>部分被处理程序捕获。
更新:正如您所建议的那样,一件好事是强制使用该值,形成。通过这种方式,你可以确保没有任何惊喜在懒惰的等待你。无论如何,这是一件好事,例如,如果您的线程返回未评估的thunk,并且实际上在另一个毫无疑问的线程中进行了评估,则可能会遇到难以发现的问题。
Module Control.Exception
已经有 evaluate
,这会强制一个thunk进入它的WHNF。我们可以很容易地扩充它,使其达到全NF:
import Control.DeepSeq
import Control.Seq
import Control.Exception
import Control.Monad
toNF ::(NFData a)=> a - > IO a
到NF =评估。 withStrategy rdeepseq
使用这个,我们可以创建 catch
,强制一个给定的动作到它的NF:
strictCatch ::(NFData a,Exception e)=> ; IO a - > (e - > IO a) - > IO a
strictCatch = catch。 (toNF =<)
这样,我们确信返回的值是完全评估的,所以我们在检查时不会有任何例外。如果您在第一个示例中使用 strictCatch
而不是 catch
,则可以验证它是否按预期工作。
Can someone please explain the difference between the behavior in ghci of the following to lines:
catch (return $ head []) $ \(e :: SomeException) -> return "good message"
returns
"*** Exception: Prelude.head: empty list
but
catch (print $ head []) $ \(e :: SomeException) -> print "good message"
returns
"good message"
Why isn't the first case catching the exception? Why are they different? And why does the first case put a double quote before the exception message?
Thanks.
Let's examine what happens in the first case:
catch (return $ head []) $ \(e :: SomeException) -> return "good message"
You create thunk head []
which is return
ed as an IO
action. This thunk doesn't throw any exception, because it isn't evaluated, so the whole call catch (return $ head []) $ ...
(which is of type IO String
) produces the String
thunk without an exception. The exception occurs only when ghci tries to print the result afterwards. If you tried
catch (return $ head []) $ \(e :: SomeException) -> return "good message"
>> return ()
instead, no exception would have been printed.
This is also the reason why you get _"* Exception: Prelude.head: empty list_. GHCi starts to print the string, which starts with "
. Then it tries to evaluate the string, which results in an exception, and this is printed out.
Try replacing return
with evaluate
(which forces its argument to WHNF) as
catch (evaluate $ head []) $ \(e :: SomeException) -> return "good message"
then you'll force the thunk to evaluate inside catch
which will throw the exception and let the handler intercept it.
In the other case
catch (print $ head []) $ \(e :: SomeException) -> print "good message"
the exception occurs inside the catch
part when print
tries to examine head []
and so it is caught by the handler.
Update: As you suggest, a good thing is to force the value, preferably to its full normal form. This way, you ensure that there are no "surprises" waiting for you in lazy thunks. This is a good thing anyway, for example you can get hard-to-find problems if your thread returns an unevaluated thunk and it is actually evaluated in another, unsuspecting thread.
Module Control.Exception
already has evaluate
, which forces a thunk into its WHNF. We can easily augment it to force it to its full NF:
import Control.DeepSeq
import Control.Seq
import Control.Exception
import Control.Monad
toNF :: (NFData a) => a -> IO a
toNF = evaluate . withStrategy rdeepseq
Using this, we can create a strict variant of catch
that forces a given action to its NF:
strictCatch :: (NFData a, Exception e) => IO a -> (e -> IO a) -> IO a
strictCatch = catch . (toNF =<<)
This way, we are sure that the returned value is fully evaluated, so we won't get any exceptions when examining it. You can verify that if you use strictCatch
instead of catch
in your first example, it works as expected.
这篇关于如何在Haskell中正确使用Control.Exception.catch?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!