代码之家  ›  专栏  ›  技术社区  ›  Bill

在haskell,懒惰和i/o是如何协同工作的?

  •  10
  • Bill  · 技术社区  · 16 年前

    我在努力加深对哈斯克尔懒惰的理解。

    我今天在想象下面的片段:

    data Image = Image { name :: String, pixels :: String }
    
    image :: String -> IO Image
    image path = Image path <$> readFile path
    

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

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

    但这就是它的工作原理吗?懒惰如何与IO兼容?无论我是否访问,都将调用readfile pixels image 或者,如果我从来没有提到运行时,运行时是否会将该thunk保留为未评估的?

    如果映像确实被偷懒地读取,那么难道不可能发生无序的I/O操作吗?例如,如果在打电话之后 image 我删除文件?现在,putstrln调用在尝试读取时将找不到任何内容。

    2 回复  |  直到 16 年前
        1
  •  17
  •   C. A. McCann Ravikant Cherukuri    16 年前

    懒惰如何与I/O兼容?

    简短回答: 不是的。


    长答案: IO 行动是严格排序的,原因和你想的差不多。当然,对结果进行的任何纯计算都可能是懒惰的;例如,如果读取文件,进行一些处理,然后打印出一些结果,则很可能不会对输出不需要的任何处理进行求值。但是,整个文件都将被读取,甚至是您从未使用过的部分。如果您想要懒惰的I/O,您有两个大致的选择:

    • 滚动你自己的显式延迟加载例程等,就像你在任何严格的语言。当然,看起来很烦人,但另一方面,haskell却提出了一个很好的严格的命令式语言。如果你想尝试一些新的有趣的东西,试着看看 Iteratees 。

    • 像个作弊的骗子一样作弊。功能 such as hGetContents 会偷懒,按需为你I/O,不问问题。有什么发现?它(技术上)打破了参照的透明度。纯代码可能会间接导致副作用,如果代码真的很复杂,有趣的事情可能会发生,包括副作用的排序。 汞含量 朋友们被执行了 using unsafeInterleaveIO ,这是……和罐头上写的一模一样。在你脸上爆炸的可能性远不及使用 unsafePerformIO ,但你要考虑到自己被警告了。

        2
  •  9
  •   Norman Ramsey    16 年前

    懒惰的I/O破坏了Haskell的纯洁。结果来自 readFile 确实是按需生产的。I/O操作发生的顺序不是固定的,因此是的,它们可能发生“无序”。在提取像素之前删除文件的问题是真的。简而言之,懒惰的i/o是一个非常方便的工具,但它是一个具有非常尖锐的边缘的工具。

    关于真实世界的书haskell有 lengthy treatment of lazy I/O 并且克服了一些陷阱。

    推荐文章