如何在Haskell中正确使用Control.Exception.catch? [英] How do I correctly use Control.Exception.catch in Haskell?

查看:151
本文介绍了如何在Haskell中正确使用Control.Exception.catch?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  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 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 returned 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆