潜入monads后,我明白它们是允许在某些上下文中链接计算的一般概念(失败,非确定性,状态等),并且它们背后没有魔法。
即使不是魔法,仍然IO monad感觉,但特别。
main
功能上述观点的原因是什么?是什么让IO如此特别?
更新:在纯代码评估顺序中无关紧要。但是在做IO时这很重要(我们希望在我们阅读之前保存客户)。根据我的理解,IO monad为我们提供了这样的订购保证。它是一般monad的属性还是IO monad特有的东西?
答案 0 :(得分:7)
你不能像其他monad那样逃避IO monad
我不确定“逃避”是什么意思。如果你的意思是,以某种方式展开monadic值的内部表示(例如list - > cons-cells序列) - 这是一个实现细节。实际上,您可以定义an IO
emulation in pure Haskell - 基本上只是State
对大量全局可用数据的定义。这将具有IO
的所有语义,但实际上没有与现实世界交互,只有模拟。
如果你的意思是,你可以从monad-nope中“提取值”,即使对于大多数纯粹的haskell monad来说,这通常也是不可能的。例如,您无法从Maybe a
(可能是Nothing
)或Reader b a
中提取值(如果b
无人居住会怎样?)
运行
IO
操作只能由main
函数
嗯,从某种意义上说,所有只能由main
函数运行。不以某种方式从main
调用的代码只会位于此处,您可以将其替换为undefined
而不更改任何内容。
IO始终位于monad变换器链的底部
是的,但情况也是如此。 ST
。
IO
monad的实现尚不清楚,源代码显示了一些Haskell内部
再次说明:实现只是一个实现细节。 IO
实现的复杂性实际上与高度优化有很大关系;对于专门的纯monad也是如此(例如attoparsec)。
As I already said),更简单的实现是可能的,它们不会像完整的优化现实世界IO
类型那样有用。
幸运的是,实施不必真的打扰你; IO
的内部可能不清楚,但实际的monadic界面外部非常简单。
纯代码评估顺序中的无关紧要
首先 - 评估订单可以在纯代码中重要!
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
是非常特别的,我确实有些人会把它称为monad(我有点不清楚IO
实现monad法律的意义是什么)。特别是,懒惰的IO 是一个巨大的麻烦制造者(最好在任何时候都避免)。
答案 1 :(得分:2)
可以对ST
monad,或者STM
monad进行类似的陈述(尽管你可以在IO
之上实际实现那个)。
基本上像Reader monad,Error monad,Writer monad等等,都只是纯粹的代码。 ST
和IO
monad是唯一确实存在不纯物质的状态(状态变异等),所以它们在纯Haskell中无法定义。他们必须是"硬连线"进入编译器的某个地方。