IO monad是什么意思(如果有的话)?
在深入monads之后,我明白他们是一个通用概念,允许在某些上下文(失败,非确定性,状态等)内链接计算,并且他们背后没有魔法。
即使不是魔法,但IO monad仍然感觉很特别。
main
函数运行 以上几点的原因是什么? 什么让IO如此特别?
更新 :纯代码评估顺序无关紧要。 但是在做IO时它确实很重要(我们在读取它之前要保存客户)。 据我所知,IO monad为我们提供了这样的订购保证。 它是一般monad的属性,还是IO monad特有的?
你不能像其他monad那样逃脱IO monad
我不确定你的意思是“逃脱”。 如果你的意思是以某种方式解开一元值的内部表示(例如list - > cons-cells序列) - 那就是一个实现细节。 事实上,你可以在纯Haskell中定义一个IO
仿真 - 基本上只是一个State
遍布全球的数据。 这将具有IO
所有语义,但是实际上没有与真实世界交互,只是模拟它。
如果你的意思是,你可以从monad中“提取数值” - 这是不可能的,即使对于大多数纯哈斯克尔单子来说也是如此。 例如,你不能从Maybe a
(可能是Nothing
)或Reader ba
提取一个值(如果b
无人居住,该怎么办?)
IO
操作只能由main
函数运行
那么,从某种意义上说,一切只能由main
功能来运行。 不以某种方式从main
调用的代码只会在那里存在,您可以用undefined
替换而不更改任何内容。
IO总是处于单变压器链的底部
确实如此,但ST
也是如此。
IO
monad的实现不清楚,源代码显示了一些Haskell内部
再次说明:实现就是一个实现细节。 IO
实现的复杂性实际上与高度优化有很大关系; 专门的纯单子也是如此(例如attoparsec)。
正如我已经说过的),更简单的实现是可能的,它们不会像成熟的优化的真实世界IO
类型那样有用。
幸运的是,实施并不需要真的打扰你; IO
的内部可能不清楚,但外部的实际一元接口非常简单。
在纯代码评估顺序中并不重要
首先 - 评估顺序可以在纯代码中起作用!
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.
但是,由于错误的纯代码评估,事实上你永远不会得到错误的非⊥结果。 这实际上并不适用于重新排序monadic动作( IO
或其他),因为改变序列顺序改变了结果动作的实际结构,而不仅仅是运行时将用来构造该结构的评估顺序。
例如(list 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
的确是非常特殊的,我认为有些人毫不犹豫地称之为单子(实际上它应该表示IO
实现单子法的含义有点不清楚)。 尤其懒惰的IO是一个巨大的麻烦制造者(并且最好在任何时候都避免)。
可以对ST
monad或者STM
monad做类似的声明(尽管你可以在IO
之上实现)。
基本上像Reader monad,Error monad,Writer monad等等,都只是纯代码。 ST
和IO
monad是唯一真正做不纯的事情(状态变异等),所以它们在纯粹的Haskell中是不可定义的。 它们必须在某处“编译”到编译器中。
上一篇: In what sense is IO monad special (if at all)?
下一篇: Functional implementation (e.g. in Haskell) of GoF's Lexi Editor application