如何正确地强制评估 IO monad 中的纯值?
How to properly force evaluation of pure value in IO monad?
我需要在 IO
monad 中强制计算纯值。我在写
C 绑定的更高级别接口。在较低的级别上,我说 newFile
函数和 freeFile
函数。 newFile
returns 一些 id,不透明对象
我已经在较低级别上定义了。你基本上不能用它做任何事情但是
使用它来释放文件 and 纯粹计算与
那个文件。
所以,我有(简化):
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
:
The value of seq a b
is bottom if a
is bottom, and otherwise equal to
b
. seq
is usually introduced to improve performance by avoiding
unneeded laziness.
A note on evaluation order: the expression seq a b
does not guarantee
that a
will be evaluated before b
. The only guarantee given by seq
is that the both a
and b
will be evaluated before seq
returns a
value. In particular, this means that b
may be evaluated before a
. If
you need to guarantee a specific order of evaluation, you must use the
function pseq
from the "parallel" package.
一个很好的说明,的确,我看到人们对秩序有不同的看法
在这种情况下的评估。 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
returns 其值时进行评估。但同样,他们中的哪一个,
x
还是 freeFile
先求值?因为我没有遇到段错误,所以它必须
是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
这将解决的另一种可能性是 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
然后无需进一步摆弄强制即可工作。
Daniel Wagner 的回答对于这个用例很好,但有时只评估列表的脊柱而不评估列表元素也是有益的(有时列表项没有合理的 NFData
实例).您不能为此使用 deepseq
,因为它会评估所有内容。但是,您可以将 seq
与此功能结合使用:
evalList :: [a] -> ()
evalList [] = ()
evalList (_:r) = evalList r
我需要在 IO
monad 中强制计算纯值。我在写
C 绑定的更高级别接口。在较低的级别上,我说 newFile
函数和 freeFile
函数。 newFile
returns 一些 id,不透明对象
我已经在较低级别上定义了。你基本上不能用它做任何事情但是
使用它来释放文件 and 纯粹计算与
那个文件。
所以,我有(简化):
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
:
The value of
seq a b
is bottom ifa
is bottom, and otherwise equal tob
.seq
is usually introduced to improve performance by avoiding unneeded laziness.A note on evaluation order: the expression
seq a b
does not guarantee thata
will be evaluated beforeb
. The only guarantee given byseq
is that the botha
andb
will be evaluated beforeseq
returns a value. In particular, this means thatb
may be evaluated beforea
. If you need to guarantee a specific order of evaluation, you must use the functionpseq
from the "parallel" package.
一个很好的说明,的确,我看到人们对秩序有不同的看法
在这种情况下的评估。 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
returns 其值时进行评估。但同样,他们中的哪一个,
x
还是 freeFile
先求值?因为我没有遇到段错误,所以它必须
是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
这将解决的另一种可能性是 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
然后无需进一步摆弄强制即可工作。
Daniel Wagner 的回答对于这个用例很好,但有时只评估列表的脊柱而不评估列表元素也是有益的(有时列表项没有合理的 NFData
实例).您不能为此使用 deepseq
,因为它会评估所有内容。但是,您可以将 seq
与此功能结合使用:
evalList :: [a] -> ()
evalList [] = ()
evalList (_:r) = evalList r