我需要在IO
monad中强制评估纯值。我正在写作
C语言绑定的更高级别接口。在较低级别,我有newFile
函数和freeFile
函数。 newFile
返回一些id,不透明对象
我已在较低级别定义。你基本上不能做任何事情
用它来释放文件和纯粹计算与之相关的东西
那个文件。
所以,我有(简化):
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path -- ‘fid’ stands for “file id”
let x = runGetter g fid
freeFile fid
return x
这是该功能的初始版本。我们需要先计算x
调用freeFile
。 (代码有效,如果我删除freeFile
它一切都很好,
但我想释放资源,你知道。)
首次尝试(我们将使用seq
来“强制”评估):
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x `seq` freeFile fid
return x
分段错误。直接进入文档
seq
:
如果
seq a b
是最低的,则a
的值是最低的,否则等于b
。通常会引入seq
以通过避免来提高性能 不必要的懒惰。关于评估顺序的说明:表达式
seq a b
不保证 在a
之前评估b
。seq
给出的唯一保证 是在a
返回a之前评估b
和seq
值。特别是,这意味着可以在b
之前评估a
。如果 您需要保证特定的评估顺序,您必须使用 函数pseq
来自&#34; parallel&#34;封装
一个很好的说明,事实上,我已经看到人们声称订单不同
在这种情况下的评估。那么pseq
呢?我需要依靠吗?
parallel
只是因为pseq
,嗯...可能还有另一种方式。
{-# LANGUAGE BangPatterns #-}
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let !x = runGetter g fid
freeFile fid
return x
分段错误。好,
that answer在我的工作中不起作用
案件。但它建议evaluate
,让我们试试吧:
Control.Exception (evaluate)
Control.Monad (void)
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
void $ evaluate x
freeFile fid
return x
分段错误。也许我们应该使用evaluate
返回的值?
Control.Exception (evaluate)
Control.Monad (void)
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x' <- evaluate x
freeFile fid
return x'
不,不好主意。也许我们可以链seq
:
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x `seq` freeFile fid `seq` return x
这很有效。但这是正确的方法吗?也许它只能起作用
一些volatile优化逻辑?我不知道。如果seq
与...关联
在这种情况下,然后根据该描述x
和freeFile
在return x
返回其值时进行评估。但是,他们中的哪一个,
首先评估x
或freeFile
?因为我没有得到段错误,所以必须这样做
是x
,但这个结果可靠吗?你知道如何强制评估吗?
在x
正确之前freeFile
?
答案 0 :(得分:9)
一个可能的问题是newFile
正在做一些懒惰的IO,并且runGetter
是一个充分懒惰的消费者,在其输出上运行seq
并不会强制所有newFile
实际发生的IO。可以使用deepseq
代替seq
:
execGetter :: NFData a => FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x `deepseq` freeFile fid
return x
这将解决的另一种可能性是runGetter
声称是纯粹的,但实际上并不是(并且是一个懒惰的制作人)。但是,如果是这种情况,那么正确的解决方法不是在此使用deepseq
,而是要从unsafePerformIO
中删除runGetter
的使用,然后使用:
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
x <- runGetter g fid
freeFile fid
return x
然后应该在不进一步摆弄强制的情况下工作。
答案 1 :(得分:0)
Daniel Wagner的答案对于这个用例很好,但有时仅评估列表的主干并使列表元素不被评估(有时列表项没有合理的NFData
实例)也是有益的。您无法使用deepseq
,因为它会评估所有内容。但是,您可以将seq
与此功能结合使用:
evalList :: [a] -> ()
evalList [] = ()
evalList (_:r) = evalList r