在深入研究 monad 之后,我了解到它们是一个通用概念,允许在某些上下文(失败、非确定性、状态等)内链接计算,并且它们背后没有魔法。
即使不是魔法,但 IO monad 仍然感觉很特别。
main
运行功能 以上几点的原因是什么?是什么让 IO 如此特别?
更新 :在纯代码评估顺序中并不重要。但是在做 IO 时确实很重要(我们想在阅读之前保存客户)。据我了解,IO monad 为我们提供了这样的排序保证。它是一般 monad 的属性还是 IO monad 特有的属性?
最佳答案
you cannot escape IO monad like you can with other monads
我不确定你所说的“逃跑”是什么意思。如果你的意思是,以某种方式解开一元值的内部表示(例如列表 -> 单元格序列)——这是一个实现细节。其实可以定义an
IO
emulation in pure Haskell – 基本上只是一个 State
通过大量全局可用的数据。这将具有 IO
的所有语义。 ,但没有实际与现实世界交互,只是对其进行模拟。如果您的意思是,您可以从 monad 中“提取值”——不,这通常是不可能的,即使对于大多数纯 haskell monad 也是如此。例如,您不能从
Maybe a
中提取值。 (可以是 Nothing
)或 Reader b a
(如果 b
无人居住怎么办?)
IO
action can only be run by themain
function
嗯,从某种意义上说,一切都只能由
main
运行。功能。未以某种方式从 main
调用的代码只会坐在那里,您可以将其替换为 undefined
没有改变任何东西。IO is always at the bottom of a monad transformers chain
是的,但对于例如,情况也是如此。
ST
.Implementation of
IO
monad is unclear and source code shows some Haskell internals
再说一遍:实现只是一个实现细节。
IO
的复杂性实现实际上与高度优化有很大关系;对于专门的纯单子(monad)也是如此(例如 attoparsec )。As I already said ),更简单的实现是可能的,但它们不会像成熟的优化现实世界
IO
那样有用类型。幸运的是,实现并不需要真正打扰您。
IO
的内部可能不清楚,但外部,实际的单子(monad)接口(interface),非常简单。in pure code evaluation order doesn't matter
首先——评估顺序在纯代码中很重要!
Prelude> take 10 $ foldr (\h t -> h `seq` (h:t)) [] [0..]
[0,1,2,3,4,5,6,7,8,9]
Prelude> take 10 $ foldr (\h t -> t `seq` (h:t)) [] [0..]
^CInterrupted.
但实际上,由于纯代码评估顺序错误,您永远不会得到错误的非⊥结果。这实际上不适用于重新排序一元 Action (
IO
或其他),因为更改序列顺序会更改结果 Action 的实际结构,而不仅仅是运行时将用于构造此结构的评估顺序。例如(列表单子(monad)):
Prelude> [1,2,3] >>= \e -> [10,20,30] >>= \z -> [e+z]
[11,21,31,12,22,32,13,23,33]
Prelude> [10,20,30] >>= \z -> [1,2,3] >>= \e -> [e+z]
[11,12,13,21,22,23,31,32,33]
说了这么多,当然
IO
非常特别,确实我认为有些人不愿称它为 monad(对于 IO
履行 monad 法则的实际含义有点不清楚)。特别是,惰性 IO 是一个巨大的麻烦制造者(最好始终避免)。
关于haskell - IO monad 在什么意义上是特殊的(如果有的话)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36596701/