Haskell IO Monad和内存使用

7

我可能没有完全理解IO Monad。

如果我编写一个预计运行数月并记录其进度的应用程序,IO Monad是否会将所有日志信息保存在RAM中直到结束?

根据IO Inside博客上的内容,Haskell将世界建模为

main :: RealWorld -> ((), RealWorld)

为了避免在Haskell代码执行期间发生IO操作,而只在应用程序从main函数返回时进行IO操作。
我可能完全误解了这一点。有人能解释一下Haskell何时实际进行IO操作吗?
2个回答

8

IO单子会一直将所有日志信息保存在内存中直到结束吗?

不是的。你不应该认为"IO单子"是执行操作的东西。它只是一种表示命令式程序的数学方式。原始的命令式程序是像getChar这样的东西;>>=用于将两个程序粘合成一个更大的命令式程序。IO单子是所有命令式程序的集合。

考虑这样一个程序:

main = putStr "Hello, " >> putStrLn "world!"

这意味着:main是一个程序,它执行程序putStr"Hello,",当它完成时,执行程序putStrLn"world!"。Haskell解释器或已编译程序除了指令指针外不需要在内存中保留任何状态,“我们在哪里以及下一步要执行什么”。

RealWorld -> ((), RealWorld)这个比喻可能会让你感到困惑,因为它似乎暗示将外部世界的状态转换为必须完全计算的新状态,然后才能更新世界以反映计算出的状态。但事实并非如此。Haskell维基对此发出了警告:

关于IO的以下故事是错误的,因为它实际上无法解释一些重要方面的IO(包括交互和并发)。


感谢您的详细解释。我已经阅读了《学习 Haskell》(目前正在第二遍阅读单子部分),并开始阅读《实际 Haskell》。除非我错过了什么,否则它们都没有明确说明 IO 是立即发生的,而是像其他单子一样“累积”到最后。我想我需要查看编译器的汇编语言输出来说服自己 :-). - Ralph
RealWorld -> ((), RealWorld) 这个比喻确实似乎暗示了变换以纯粹的形式发生,并且只在最后完全被发出。我猜 IO 操作的基础实现实际上是“按需”发生的。 - Ralph
6
理解 IO Monad 的关键在于它累积的是“程序”,而非实际的输入和输出。很多 Haskell 教程都没有很好地解释这一点。 - Fred Foo
1
以这种方式考虑 RealWorld -> ((), RealWorld) 不能很好地解释 forever (putStrLn "hello!") 的工作原理(它确实可以工作,这表明 IO 没有在任何地方累积)。 - Ben Millwood
"IO单子是所有命令式程序的集合" - 这是一个非常有趣的表述方式。 - Dan Burton

4

IO单子会一直将日志信息保存在RAM中直到最后吗?

不会。假设您正在使用明智的日志记录策略。

Haskell在程序要求结果时执行IO操作;对于大多数操作,这是立即发生的。(唯一的例外是延迟文件输入库,在这种情况下,文件可能不会被读取直到数据被程序使用)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接