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

时间:2016-04-13 11:16:39

标签: haskell functional-programming monads io-monad

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

即使不是魔法,仍然IO monad感觉,但特别。

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

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

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

2 个答案:

答案 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等等,都只是纯粹的代码。 STIO monad是唯一确实存在不纯物质的状态(状态变异等),所以它们在纯Haskell中无法定义。他们必须是"硬连线"进入编译器的某个地方。