IO monad在什么意义上是特殊的(如果有的话)?

dam*_*uar 3 monads haskell functional-programming io-monad

潜入monad后,我明白它们是允许在某些上下文中链接计算的一般概念(失败,非确定性,状态等),并且它们背后没有任何魔力.

IO monad感觉即使不是魔术,但特别.

  • 你不能像其他monad那样逃避IO monad
  • IO操作只能由该main函数运行
  • IO始终位于monad变换器链的底部
  • IO monad的实现尚不清楚,源代码显示了一些Haskell内部

上述观点的原因是什么?是什么让IO如此特别?

更新:在纯代码评估顺序无关紧要.但是在做IO时这很重要(我们希望在我们阅读之前保存客户).根据我的理解,IO monad为我们提供了这样的订购保证.它是一般monad的属性还是IO monad特有的东西?

lef*_*out 7

你不能像其他monad那样逃避IO monad

我不确定你的"逃避"是什么意思.如果你的意思是,以某种方式打开monadic值的内部表示(例如list - > cons-cells序列) - 这是一个实现细节.事实上,你可以定义一个IO 仿真纯哈斯克尔 -基本上是一个State在很多全球可用的数据.这将具有所有语义IO,但实际上没有与现实世界交互,只有它的模拟.

如果你的意思是,你可以从monad-nope中"提取值",即使对于大多数纯粹的haskell monad来说,这通常也是不可能的.例如,您无法从Maybe a(可能Nothing)或Reader b a(如果b无人居住?)中提取值

IO动作只能由main函数运行

嗯,从某种意义上说,一切都只能由main函数运行.不以某种方式调用的代码main只会坐在那里,你可以替换它undefined而不改变任何东西.

IO始终位于monad变换器链的底部

没错,但例如也是如此ST.

IOmonad的实现尚不清楚,源代码显示了一些Haskell内部

再说一遍:实现只是一个实现细节.IO实现的复杂性实际上与高度优化有很大关系; 对于专门的纯monad(例如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.
Run Code Online (Sandbox Code Playgroud)

但实际上,由于错误的纯代码评估,你永远不会得到错误的非⊥结果.这实际上不适用于重新排序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]
Run Code Online (Sandbox Code Playgroud)

所有这一切,当然IO是非常特别的,事实上我认为有些人会毫不犹豫地将其称为monad(它有点不清楚它实际上应该IO用来实现monad法则).特别是,懒惰IO是一个巨大的麻烦制造者(最好在任何时候都避免).


Mat*_*hid 2

ST可以对monad 或可以说是monad做出类似的陈述STM(尽管您实际上可以在 之上实现该陈述IO)。

基本上,像 Reader monad、Error monad、Writer monad 等都只是纯代码。和monadSTIO唯一真正做不纯粹的事情(状态突变等)的,所以它们在纯粹的 Haskell 中是不可定义的。它们必须“硬连线”到编译器的某个地方。