在对,和进行大量试验后,按照@Roman Cheplyaka 的建议,我想出了这个解决方案,现在我看到了最终结果,这似乎非常明显:evaluate
force
try
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
而不是a
then 你最终会得到一个 type 的对象IO (IO a)
,就像evaluate
type一样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
并直接强制严格读取文件。