IO Monad纯粹是什么意思?

我已经把IO monad描述为一个国家单体,国家是“真实的世界”。 这种IO方法的支持者认为,这使得IO操作是纯粹的,就像在透明中一样。 这是为什么? 从我的angular度来看,IO monad中的代码有很多可观察到的副作用。 另外,难道不可能像真实世界的函数那样描述任何非纯粹的function吗? 例如,我们不能把C的malloc看作是一个函数,它需要一个RealWorld和一个Int,并且返回一个指针和一个RealWorld ,就像RealWorld隐含的IO monad一样。

注意:我知道monad是什么,以及它是如何使用的。 请不要回应一个随机monad教程的链接,除非它特别强调我的问题。

我认为我所听到的最好的解释实际上是最近在SO上。 IO Foo是创buildFoo的配方。 另一种常见的,更直接的说法是,它是一个“产生Foo程序”。 它可以被执行(很多次)创build一个Foo或死亡尝试。 配方/程序的执行是我们最终想要的(否则,为什么要写一个?),但是在我们的代码中由IO动作表示的东西就是配方本身。

这个配方是一个纯粹的价值,就像一个String是一个纯粹的价值。 食谱可以以有趣的,有时令人吃惊的方式进行组合和操纵,但是这些食谱可以组合的许多方式(除了非纯粹的unsafePerformIOunsafeCoerce等)都是完全透明的,确定的,东东。 由此产生的配方绝对不以任何方式,除了从它build立起来的食谱以外的任何状态。

另外,难道不可能像真实世界的函数那样描述任何非纯粹的function吗? 例如,我们不能把C的malloc看作是一个函数,它需要一个RealWorld和一个Int,并且返回一个指针和一个RealWorld,就像RealWorld隐含的IO monad一样。

当然 …

函数式编程的整个概念是把程序描述成一个小型的,独立的计算的组合,构成了更大的计算。

有了这些独立的计算,您将获得很多好处,从简洁的程序到高效且高效的并行化代码,懒惰到控制按预期stream动的严格保证 – 没有任何干扰或破坏任意数据的机会。

现在 – 在某些情况下(如IO),我们需要不纯的代码。 涉及这些操作的计算不能独立 – 它们可能会改变另一个计算的任意数据。

关键是 – Haskell 总是纯粹的IO不会改变这一点。

所以,我们不纯的非独立代码必须得到一个共同的依赖 – 我们必须通过一个RealWorld 。 因此,无论我们想要运行什么有状态的计算,我们都必须通过这个RealWorld来应用我们的变化 – 而其他任何有状态的计算想要看到或做出的更改也必须知道RealWorld

无论是通过IO monad明确还是隐式完成,都是无关紧要的。 你build立一个Haskell程序作为一个巨大的计算转换数据,其中一部分是RealWorld

一旦最初的main :: IO ()被调用,当你的程序以当前的真实世界作为参数运行时,这个真实的世界就会通过所有不纯的计算来进行,就像数据在一个State 。 这是monadic >>=照顾。

RealWorld没有得到的地方(就像在纯粹的计算中,或者没有任何>>= -ing到main ),没有任何事情可以做。 而且在哪里得到,这是由一个(隐式)参数的纯函数传递发生的。 这就是为什么

 let foo = putStrLn "AAARGH" in 42 

什么都不做 – 为什么IO monad和其他东西一样纯粹。 这个代码里面发生的事情当然可能是不纯的,但是它全部被捕获,不会干扰非连接的计算。

假设我们有这样的东西:

 animatePowBoomWhenHearNoiseInMicrophone :: TimeDiff -> Sample -> IO () animatePowBoomWhenHearNoiseInMicrophone levelWeightedAverageHalfLife levelThreshord = ... programA :: IO () programA = animatePowBoomWhenHearNoiseInMicrophone 3 10000 programB :: IO () programB = animatePowBoomWhenHearNoiseInMicrophone 3 10000 

这里有一个观点:

animatePowBoomWhenHearNoiseInMicrophone是一个纯函数,它的结果对于相同的inputprogramBprogramB完全相同。 你可以做main = programA main = programBmain = programB ,它会完全一样的。

animatePowBoomWhenHearNoiseInMicrophone是一个接收两个参数并导致程序描述的函数。 Haskell运行时可以执行这个描述,如果你设置main或者通过main通过绑定包括它。

什么是IOIO是用于描述命令式程序的DSL,以“pure-haskell”数据结构和函数编码。

“complete-haskell”又名GHC是“pure-haskell”的实现,也是IO解码器/执行器的必要实现。

这很简单归结为外延的平等 :

如果你要调用getLine两次,那么两次调用都会返回一个IO String ,每次看起来都是一样的。 如果要编写一个函数来获取2个IO String并返回一个Bool来表示它们之间检测到的差异,则不可能从任何可观察的属性中检测到任何差异。 它不能问任何其他函数是否相等,任何使用>>=尝试都必须返回IO中所有等于外部的东西

即使它的标题有点奇怪(因为它不能精确地匹配内容),下面的haskell-cafe线程包含了关于Haskell的不同IO模型的一个很好的讨论。

http://www.mail-archive.com/haskell-cafe@haskell.org/msg79613.html

那么这就是我们在大学教过的东西 –

当函数总是返回指定input的相同值时(或者相同的expression式总是在相同的上下文中计算相同的值),函数是引用透明的。 因此,例如,如果getChartypes签名是just () -> CharChar ,那么getChar就不会是透明的,因为如果使用相同的参数多次调用此函数,可能会得到不同的结果。

但是,如果引入IO monad,那么getChar可以具有IO Chartypes,并且此types只有一个值 – IO Char 。 所以getChar allways重新获得相同的价值,无论是关键的用户真正按下。

但是你仍然能够从这个IO Char事件中“获得”底层的价值。 那么,不是真的得到,而是使用绑定运算符( >>= )传递给另一个函数,所以您可以使用用户在程序中进一步input的字符。

Monads的发明者说:用不纯的语言,像tick这样的操作可以用types() – >()的函数表示。 虚假的参数()需要延迟效果,直到函数被应用,并且由于输出types是(),人们可能会猜测函数的目的在于副作用。 相反,在这里,tick的types是M():不需要伪造的参数,M的外观明确指出可能发生什么样的效果

我不明白M()如何使空的参数列表(,),更less的虚假,但瓦德很清楚Monad只是表明一种副作用,他们并没有消除它 。 哈斯克尔的追随者似乎欺骗我们自己,当陈述单子消除杂质。

我会让Martin Odersky 回答这个问题

IO monad并不是纯粹的function。 它只是很明显,它是不纯的。

听起来很清楚。