【问题标题】:How do laziness and I/O work together in Haskell?懒惰和 I/O 如何在 Haskell 中协同工作?
【发布时间】:2011-02-16 04:32:34
【问题描述】:

我正在尝试更深入地了解 Haskell 中的惰性。

我今天在想象下面的 sn-p:

data Image = Image { name :: String, pixels :: String }

image :: String -> IO Image
image path = Image path <$> readFile path

这里的吸引力在于我可以简单地创建一个 Image 实例并传递它;如果我需要图像数据,它将被懒惰地读取 - 如果不需要,将避免读取文件的时间和内存成本:

 main = do
   image <- image "file"
   putStrLn $ length $ pixels image

但它实际上是这样工作的吗?惰性如何与 IO 兼容?无论我是否访问pixels image,都会调用 readFile,或者如果我从不引用它,运行时是否会留下未评估的 thunk?

如果图像确实是延迟读取的,那么 I/O 操作是否可能会发生乱序?例如,如果在调用image 后立即删除文件怎么办?现在 putStrLn 调用在尝试读取时将一无所获。

【问题讨论】:

    标签: haskell lazy-evaluation


    【解决方案1】:

    惰性如何与 I/O 兼容?

    简短回答:不是。


    长答案:IO 操作是严格按顺序排列的,这几乎是您所想的原因。当然,对结果进行的任何纯计算都可能是惰性的;例如,如果您读入一个文件,进行一些处理,然后打印出一些结果,则很可能不会评估输出不需要的任何处理。但是,将读取整个文件,甚至是您从未使用过的部分。如果你想要惰性 I/O,你大致有两种选择:

    • 滚动您自己的显式延迟加载例程等,就像使用任何严格的语言一样。看起来很烦人,理所当然,但另一方面,Haskell 是一种很好的严格的命令式语言。如果您想尝试一些新的有趣的东西,请尝试查看Iteratees

    • 像作弊者一样作弊。函数such as hGetContents 会为你做懒惰的按需 I/O,不问任何问题。有什么问题?它(技术上)打破了参照透明度。纯代码可能会间接导致副作用,如果您的代码真的很复杂,可能会发生涉及副作用排序的有趣事情。 hGetContents 和朋友们实现了using unsafeInterleaveIO,这就是......它在锡上所说的。它远没有使用unsafePerformIO 那样在你的脸上炸毁,但请考虑一下自己。

    【讨论】:

    • 感谢您的回答!事实上,正是 RWH 对 hGetContents 的描述让我对这个问题感到困惑。我没有意识到这是一个特殊情况,并在下面使用了不安全的 IO 调用。所以基本上,我的示例在处理 readFile 操作后立即读取文件?如果是这样,那似乎更加一致。
    • @Bill:这是 readFile 的实现,直接来自 GHC 的标准库:readFile name = openFile name ReadMode &gt;&gt;= hGetContents 所以不,您的示例属于“作弊者”类别。也就是说,惰性 I/O 函数通常对于大多数日常实际使用来说足够安全,所以除非纯度对您非常重要,否则不要太担心。
    • 我知道 Oleg 说 unsafeInterleaveIO 破坏了引用透明度,但我不同意。我会说它只是不确定的,就像IO monad 中的许多东西一样。 getCurrentTime 是否会破坏引用透明度,因为我可以使用它来确定两个外部相等函数中的哪一个更有效地实现?
    • @Reid Barton:unsafeInterleaveIO 的问题在于它让不确定性可能以有限的方式从IO 中泄露出来。据我所知,没有人证明这一特殊问题会在即使是有些合理的代码中引起有意义的问题,但这仅仅是“缺乏反例的证明”。 Oleg 设计的纯函数示例,其结果取决于计算参数的顺序,原则上对我来说足够有说服力。实际上,这并不重要。
    【解决方案2】:

    延迟 I/O 破坏了 Haskell 的纯洁性。 readFile 的结果确实是按需懒惰地产生的。 I/O 操作发生的顺序不是固定的,所以是的,它们可能“无序”发生。在拉取像素之前删除文件的问题是真实存在的。简而言之,惰性 I/O 是一种极大的便利,但它是一种边缘非常锋利的工具。

    关于 Real World Haskell 的书有一个 lengthy treatment of lazy I/O 并讨论了一些陷阱。

    【讨论】:

      猜你喜欢
      • 2015-04-02
      • 2015-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      相关资源
      最近更新 更多