Mar*_*ark 8 haskell lazy-evaluation
我需要在IOmonad中强制评估纯值.我正在为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
Run Code Online (Sandbox Code Playgroud)
这是该函数的初始版本.我们需要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
Run Code Online (Sandbox Code Playgroud)
分段故障.直接进入以下文档
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
Run Code Online (Sandbox Code Playgroud)
分段故障.那么,
这个答案对我来说不起作用.但它表明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
Run Code Online (Sandbox Code Playgroud)
分段故障.也许我们应该使用返回的值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'
Run Code Online (Sandbox Code Playgroud)
不,不好主意.也许我们可以链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
Run Code Online (Sandbox Code Playgroud)
这有效.但这是正确的方法吗?也许它只能由于一些易变的优化逻辑而起作用?我不知道.如果seq在这种情况下左侧的关联,那么根据该描述,x并且freeFile
在return x返回其值时进行评估.但是,再次,他们中的哪一个,
x或者freeFile首先评估?既然我没有得到seg故障,那一定是x,但这个结果是否可靠?你知道如何x在freeFile 适当之前强制评估
吗?
一个可能的问题是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
Run Code Online (Sandbox Code Playgroud)
这将解决的另一种可能性runGetter是声称是纯粹的,但实际上不是(并且是一个懒惰的生产者).但是,如果是这种情况,那么正确的修复不是在deepseq这里使用,而是为了消除unsafePerformIOfrom 的使用runGetter,然后使用:
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
x <- runGetter g fid
freeFile fid
return x
Run Code Online (Sandbox Code Playgroud)
这应该工作,而不用进一步摆弄强迫.