5

我试图从文件中读取一些值并捕获可能发生的每个异常(以“更容易请求宽恕而不是许可”的心态)。我在捕捉Prelude.read: no parse异常时遇到了麻烦。告诉try它应该捕获我tryAny用显式类型定义的每个异常,据SomeException我所知,它是每个异常的“超级类型”:

import Control.Exception (try,SomeException)
tryAny :: IO a -> IO (Either SomeException a)
tryAny = try

tryAny似乎能够捕捉到 IO 错误:

> tryAny (fromFile "nonExistingFileName")
Left nonExistingFileName: openFile: does not exist (No such file or directory)

但是不会捕获读取错误:

> tryAny (return ((read "a")::Int))
Right *** Exception: Prelude.read: no parse

我能做些什么来捕捉每个异常?

4

4 回答 4

4

return不评估它的论点,因此不会抛出任何异常。tryAny当您尝试打印结果时,评估发生在 之外。

为此使用evaluate(可能与forcefrom一起使用Control.DeepSeq,具体取决于您的实际情况)。

于 2013-11-04T13:38:24.427 回答
2

在对,和进行大量试验后,按照@Roman Cheplyaka 的建议,我想出了这个解决方案,现在我看到了最终结果,这似乎非常明显:evaluateforcetry

import Control.DeepSeq (force,NFData)
import Control.Exception (try,SomeException)

tryAnyStrict :: NFData a => IO a -> IO (Either SomeException a)
tryAnyStrict = try.evaluateIoCompletely

evaluateIoCompletely :: NFData a => IO a -> IO a
evaluateIoCompletely ioA = do
    a <- ioA
    evaluate $ force a

该解决方案将流程拆分为一个函数,该函数将强制执行严格的评估并抛出所有异常,以及一个将捕获这些异常的包装器。force返回一个纯 haskell 对象,必须首先对其自身进行评估以强制对a. 从文档中:

force x = x `deepseq` x

force x 完全计算 x,然后返回它。请注意,力 x 仅在需要力 x 本身的值时才执行评估,因此本质上它将浅层评估转变为深度评估。

在我之前的尝试中,这个实现有一些微妙的地方出错了:

  • 如果你ioA直接使用作为输入force而不是athen 你最终会得到一个 type 的对象IO (IO a),就像evaluatetype一样b -> IO b。这个直接的问题可以通过 type 的一些 unwrap 函数来解决IO(IO a) -> IO a,但是你会遇到 typeclass 假设的问题NFData a,现在需要NFData (IO a)。但是因为IO a似乎没有可用的实例声明,至少对于a::(String,String).
  • 而不是使用两个函数,您可以只使用tryAnyStrict = do a <- ioA; (try.evaluate.force) a. 那么问题是a <- ioA不会捕获来自的异常,例如如果ioA是结果serialized <- readFile; return $ read serialized并且要从中读取的文件不存在。
  • 你不能try.evaluateIoCompletely在没有类型信息的情况下内联代码,因为 try 需要知道它应该捕获哪个异常。现在通过返回类型提供此信息,该返回类型将tryAnyStrict类型解析为etry :: Exception e => IO a -> IO (Either e a)一般的异常类型SomeException

现在,在我开始tryAnyStrict工作后,我仍然遇到延迟评估的问题,因为如果a <- ioA失败 - 其中 ioA 是read从文件中延迟读取的字符串的结果 - (例如,文件内容没有必要的格式read),那么文件是仍然没有完全阅读,因此仍然打开。随后对该文件的写入将失败(“openFile:资源繁忙”)。所以......我实际上可能会再次使用@kqr 和@Roman Cheplyaka 的建议重新编写整个代码readMayreadMaybe并直接强制严格读取文件。

于 2013-11-05T01:11:29.853 回答
1

我有同样的问题,并没有readMaybe按照某些评论中的建议找到。但我发现另一个有用的读取变体解决了我的问题 - readIO

readIO 函数与 read 类似,只是它向 IO monad 发出解析失败的信号,而不是终止程序。

使用它,我能够捕获并处理程序中的所有错误。

于 2015-04-25T22:46:51.757 回答
-1

如果您可以使用安全异常

您可以import Control.Exception.Safe使用tryAnyDeep

tryDeep :: (C.MonadCatch m, MonadIO m, E.Exception e, NFData a) => m a -> m (Either e a)
tryDeep f = catch (liftM Right (evaluateDeep f)) (return . Left)
tryAnyDeep :: (C.MonadCatch m, MonadIO m, NFData a) => m a -> m (Either SomeException a)
tryAnyDeep = tryDeep

但它的解决方案期望一个有deriving (NFData)

于 2017-09-12T03:49:58.253 回答