我可能没有完全理解IO Monad。
如果我编写一个预计运行数月并记录其进度的应用程序,IO Monad是否会将所有日志信息保存在RAM中直到结束?
根据IO Inside博客上的内容,Haskell将世界建模为
main :: RealWorld -> ((), RealWorld)
为了避免在Haskell代码执行期间发生IO操作,而只在应用程序从main函数返回时进行IO操作。
我可能完全误解了这一点。有人能解释一下Haskell何时实际进行IO操作吗?
IO单子会一直将所有日志信息保存在内存中直到结束吗?
不是的。你不应该认为"IO单子"是执行操作的东西。它只是一种表示命令式程序的数学方式。原始的命令式程序是像getChar
这样的东西;>>=
用于将两个程序粘合成一个更大的命令式程序。IO单子是所有命令式程序的集合。
考虑这样一个程序:
main = putStr "Hello, " >> putStrLn "world!"
main
是一个程序,它执行程序putStr"Hello,"
,当它完成时,执行程序putStrLn"world!"
。Haskell解释器或已编译程序除了指令指针外不需要在内存中保留任何状态,“我们在哪里以及下一步要执行什么”。
RealWorld -> ((), RealWorld)
这个比喻可能会让你感到困惑,因为它似乎暗示将外部世界的状态转换为必须完全计算的新状态,然后才能更新世界以反映计算出的状态。但事实并非如此。Haskell维基对此发出了警告:
关于IO的以下故事是错误的,因为它实际上无法解释一些重要方面的IO(包括交互和并发)。
IO单子会一直将日志信息保存在RAM中直到最后吗?
不会。假设您正在使用明智的日志记录策略。
Haskell在程序要求结果时执行IO操作;对于大多数操作,这是立即发生的。(唯一的例外是延迟文件输入库,在这种情况下,文件可能不会被读取直到数据被程序使用)。
RealWorld -> ((), RealWorld)
这个比喻确实似乎暗示了变换以纯粹的形式发生,并且只在最后完全被发出。我猜 IO 操作的基础实现实际上是“按需”发生的。 - RalphRealWorld -> ((), RealWorld)
不能很好地解释forever (putStrLn "hello!")
的工作原理(它确实可以工作,这表明 IO 没有在任何地方累积)。 - Ben Millwood