在对,和进行大量试验后,按照@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类型解析为e最try :: Exception e => IO a -> IO (Either e a)一般的异常类型SomeException。
现在,在我开始tryAnyStrict工作后,我仍然遇到延迟评估的问题,因为如果a <- ioA失败 - 其中 ioA 是read从文件中延迟读取的字符串的结果 - (例如,文件内容没有必要的格式read),那么文件是仍然没有完全阅读,因此仍然打开。随后对该文件的写入将失败(“openFile:资源繁忙”)。所以......我实际上可能会再次使用@kqr 和@Roman Cheplyaka 的建议重新编写整个代码readMay,readMaybe并直接强制严格读取文件。