首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何正确地强制执行IO单的纯价值评估?

如何正确地强制执行IO单的纯价值评估?
EN

Stack Overflow用户
提问于 2015-10-29 20:19:30
回答 2查看 913关注 0票数 8

我需要强制评估纯粹的价值在IO单。我正在编写C绑定的高级接口。在较低的层次上,比如newFile函数和freeFile函数。newFile返回一些我在较低级别上定义的id,不透明对象。基本上,您不能对它做任何事情,但是要使用它来释放文件,并纯粹计算与该文件相关的内容。

因此,我已(简化):

代码语言:javascript
复制
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来“强制”评估):

代码语言:javascript
复制
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不能保证ab之前被计算。seq提供的唯一保证是在ab返回值之前对seq进行计算。特别是,这意味着可以在b之前对a进行评估。如果需要保证计算的特定顺序,则必须使用“并行”包中的函数pseq

一个很好的注意,的确,我看到人们声称不同的事情的评价顺序在这个例子。那pseq呢?我需要依赖parallel吗?就因为pseq,嗯,…也许还有别的办法。

代码语言:javascript
复制
{-# 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,让我们也来试试:

代码语言:javascript
复制
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返回的值

代码语言:javascript
复制
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

代码语言:javascript
复制
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与左关联,那么当xfreeFile返回其值时,将根据该描述计算return xfreeFile。但是,首先评估的是x还是freeFile?因为我没有seg故障,所以它一定是x,但是这个结果可靠吗?你知道如何在x之前强制进行freeFile评估吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-10-29 20:44:22

一个可能的问题是,newFile正在执行一些懒惰的IO,而runGetter是一个非常懒惰的消费者,因此在其输出上运行seq并不会迫使newFile的所有IO实际发生。

代码语言:javascript
复制
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的使用,然后使用:

代码语言:javascript
复制
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
  fid <- newFile path
  x <- runGetter g fid
  freeFile fid
  return x

这样就可以不用再摆弄强迫了。

票数 9
EN

Stack Overflow用户

发布于 2015-10-31 01:48:39

对于这个用例,Daniel的回答是很好的,但有时只计算列表的脊柱,而不对list元素进行评估(有时列表项没有合理的NFData实例)也是有益的。您不能为此使用deepseq,因为它会评估所有内容。但是,您可以将seq与此函数组合起来:

代码语言:javascript
复制
evalList :: [a] -> ()
evalList [] = ()
evalList (_:r) = evalList r
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33423827

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档