Bil*_*ill 10 haskell lazy-evaluation
我正在努力深入了解Haskell中的懒惰.
我今天想象的是以下片段:
data Image = Image { name :: String, pixels :: String }
image :: String -> IO Image
image path = Image path <$> readFile path
Run Code Online (Sandbox Code Playgroud)
这里的吸引力是我可以简单地创建一个Image实例并传递它; 如果我需要图像数据,它将被懒惰地读取 - 如果不是,将避免读取文件的时间和内存成本:
main = do
image <- image "file"
putStrLn $ length $ pixels image
Run Code Online (Sandbox Code Playgroud)
但它是如何实际工作的?懒惰如何与IO兼容?readFile是否会被调用,无论我是否访问,pixels image或者如果我从未引用它,运行时是否会将该thunk评估为未评估?
如果确实懒惰地读取了图像,那么I/O操作是否可能无序发生?例如,如果在调用后立即image删除文件怎么办?现在,putStrLn调用在尝试读取时将找不到任何内容.
C. *_*ann 17
懒惰如何与I/O兼容?
简短回答:事实并非如此.
答案IO很长:行动是严格按顺序排列的,这几乎是你想到的原因.当然,对结果进行的任何纯计算都可能是懒惰的; 例如,如果您读入文件,进行一些处理,然后打印出一些结果,则可能不会评估输出不需要的任何处理.但是,整个文件将被读取,甚至是您从不使用的部分.如果你想要懒惰的I/O,你有两个选择:
滚动你自己的显式延迟加载例程,就像你在任何严格的语言中一样.似乎令人讨厌,被授予,但另一方面,Haskell制作了一个严格的,严格的命令式语言.如果你想尝试新的和有趣的东西,试试看Iteratees.
作弊骗子作弊.像hGetContents你这样的函数会为你做懒惰的按需I/O,没有问题.有什么收获?它(技术上)打破了参考透明度.纯代码可以间接导致副作用,如果您的代码真的错综复杂,可能会发生涉及副作用排序的有趣事情.hGetContents和朋友们一起使用unsafeInterleaveIO,这就是......正如它所说的那样.它远没有像使用那样在你的脸上爆炸unsafePerformIO,但请考虑自己警告.