我试图更深入地了解 Haskell 中的惰性。
我今天想象了以下片段:
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 兼容?无论我是否访问都会调用readFilepixels image
或者如果我从不引用它,运行时会不会对该 thunk 进行评估?
如果图像确实是延迟读取的,那么 I/O 操作是否可能会无序发生?例如,如果在调用后立即怎么办?image
我删除文件?现在 putStrLn 调用在尝试读取时将找不到任何内容。
惰性如何与 I/O 兼容?
简短回答:事实并非如此。
长答案:IO
操作是严格按顺序进行的,其原因几乎与您所想到的一样。当然,任何对结果进行的纯计算都可能是惰性的;例如,如果您读入文件,进行一些处理,然后打印出一些结果,则可能不会评估输出不需要的任何处理。但是,整个文件都会被读取,甚至是您从未使用过的部分。如果您想要惰性 I/O,大约有两种选择:
滚动您自己的显式延迟加载例程等,就像使用任何严格的语言一样。看起来很烦人,这是理所当然的,但另一方面,Haskell 是一种很好的、严格的、命令式的语言。如果您想尝试一些新的有趣的东西,请尝试查看迭代者 http://okmij.org/ftp/Streams.html.
像一个作弊的骗子一样作弊。功能such as hGetContents http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/System-IO.html#v%3AhGetContents将为您执行惰性的、按需的 I/O,不问任何问题。有什么问题吗?它(从技术上讲)破坏了引用透明度。纯代码可能会间接导致副作用,如果您的代码确实很复杂,则可能会发生涉及副作用排序的有趣事情。hGetContents
和朋友们都实施了using unsafeInterleaveIO http://www.haskell.org/ghc/docs/6.12.1/html/libraries/base/System-IO-Unsafe.html#v%3AunsafeInterleaveIO,这就是……正如罐头上所说的那样。它远不如使用unsafePerformIO
,但请考虑自己受到警告。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)