我需要强制评估纯粹的价值在IO单。我正在编写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和b返回值之前对seq进行计算。特别是,这意味着可以在b之前对a进行评估。如果需要保证计算的特定顺序,则必须使用“并行”包中的函数pseq。
一个很好的注意,的确,我看到人们声称不同的事情的评价顺序在这个例子。那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这个很管用。但这是正确的方法吗?也许它只是因为某些易失性的优化逻辑才起作用?我不知道。如果在这种情况下,seq与左关联,那么当x和freeFile返回其值时,将根据该描述计算return x和freeFile。但是,首先评估的是x还是freeFile?因为我没有seg故障,所以它一定是x,但是这个结果可靠吗?你知道如何在x之前强制进行freeFile评估吗?
发布于 2015-10-29 20:44:22
一个可能的问题是,newFile正在执行一些懒惰的IO,而runGetter是一个非常懒惰的消费者,因此在其输出上运行seq并不会迫使newFile的所有IO实际发生。
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,而是在runGetter中消除unsafePerformIO的使用,然后使用:
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
x <- runGetter g fid
freeFile fid
return x这样就可以不用再摆弄强迫了。
发布于 2015-10-31 01:48:39
对于这个用例,Daniel的回答是很好的,但有时只计算列表的脊柱,而不对list元素进行评估(有时列表项没有合理的NFData实例)也是有益的。您不能为此使用deepseq,因为它会评估所有内容。但是,您可以将seq与此函数组合起来:
evalList :: [a] -> ()
evalList [] = ()
evalList (_:r) = evalList rhttps://stackoverflow.com/questions/33423827
复制相似问题