Haskell中的惰性和I / O如何协同工作? [英] How do laziness and I/O work together in Haskell?

查看:90
本文介绍了Haskell中的惰性和I / O如何协同工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



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

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

image :: String - > IO Image
image path =图像路径< $> readFile path

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

  main = do 
image< - imagefile
putStrLn $ length $ pixels image

但是,它是如何实际工作的?懒惰是如何与IO兼容的?无论我是否访问像素图像,是否会调用readFile,或者如果我从不引用它,运行时是否会将该thunk评估为未评估?

如果图像确实是懒懒地读取的,那么I / O操作可能不按顺序发生吗?例如,如果在调用 image 后立即删除文件?

解决方案


懒惰是如何兼容的I / O?

简短回答:不是。




长答案: IO 操作是严格按照顺序排列的, 。当然,对结果所做的任何纯粹的计算都可能是懒惰的。例如,如果您读取文件,执行一些处理,然后打印出一些结果,则可能不会评估输出不需要的任何处理。然而,整个文件将被读取,甚至你永远不会使用的部分。如果你想懒惰的I / O,你大致有两种选择:




  • 滚动你自己的显式延迟加载例程等等,就像你会用任何严格的语言一样。看起来很烦人,但是另一方面,Haskell制定了一个严格而严格的语言。如果您想尝试一些新鲜有趣的内容,请尝试查看迭代器


  • 作弊作弊。功能例如 hGetContents 将为您执行懒惰的按需I / O,不会提出任何问题。有什么收获?它(技术上)违反了参考透明度。纯粹的代码可以间接导致副作用,如果代码真的很复杂,有趣的事情可能会发生,包括副作用的排序。 hGetContents 和朋友已实现使用 unsafeInterleaveIO ,这正是它所说的。在使用 unsafePerformIO 的时候,你的面孔很可能会炸毁你,但应该考虑到自己的警告。



I'm trying to get a deeper understanding of laziness in Haskell.

I was imagining the following snippet today:

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

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

The appeal here is that I could simply create an Image instance and pass it around; if I need the image data it would be read lazily - if not, the time and memory cost of reading the file would be avoided:

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

But is that how it actually works? How is laziness compatible with IO? Will readFile be called regardless of whether I access pixels image or will the runtime leave that thunk unevaluated if I never refer to it?

If the image is indeed read lazily, then isn't it possible I/O actions could occur out of order? For example, what if immediately after calling image I delete the file? Now the putStrLn call will find nothing when it tries to read.

解决方案

How is laziness compatible with I/O?

Short answer: It isn't.


Long answer: IO actions are strictly sequenced, for pretty much the reasons you're thinking of. Any pure computations done with the results can be lazy, of course; for instance if you read in a file, do some processing, and then print out some of the results, it's likely that any processing not needed by the output won't be evaluated. However, the entire file will be read, even parts you never use. If you want lazy I/O, you have roughly two options:

  • Roll your own explicit lazy-loading routines and such, like you would in any strict language. Seems annoying, granted, but on the other hand Haskell makes a fine strict, imperative language. If you want to try something new and interesting, try looking at Iteratees.

  • Cheat like a cheating cheater. Functions such as hGetContents will do lazy, on-demand I/O for you, no questions asked. What's the catch? It (technically) breaks referential transparency. Pure code can indirectly cause side effects, and funny things can happen involving ordering of side effects if your code is really convoluted. hGetContents and friends are implemented using unsafeInterleaveIO, which is... exactly what it says on the tin. It's nowhere near as likely to blow up in your face as using unsafePerformIO, but consider yourself warned.

这篇关于Haskell中的惰性和I / O如何协同工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆