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
是一个纯粹的价值。 食谱可以以有趣的,有时令人吃惊的方式进行组合和操纵,但是这些食谱可以组合的许多方式(除了非纯粹的unsafePerformIO
, unsafeCoerce
等)都是完全透明的,确定的,东东。 由此产生的配方绝对不以任何方式,除了从它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
是一个纯函数,它的结果对于相同的inputprogramB
和programB
完全相同。 你可以做main = programA
main = programB
或main = programB
,它会完全一样的。
animatePowBoomWhenHearNoiseInMicrophone
是一个接收两个参数并导致程序描述的函数。 Haskell运行时可以执行这个描述,如果你设置main
或者通过main
通过绑定包括它。
什么是IO
? IO
是用于描述命令式程序的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式总是在相同的上下文中计算相同的值),函数是引用透明的。 因此,例如,如果getChar
types签名是just () -> Char
或Char
,那么getChar
就不会是透明的,因为如果使用相同的参数多次调用此函数,可能会得到不同的结果。
但是,如果引入IO monad,那么getChar
可以具有IO Char
types,并且此types只有一个值 – IO Char
。 所以getChar
allways重新获得相同的价值,无论是关键的用户真正按下。
但是你仍然能够从这个IO Char
事件中“获得”底层的价值。 那么,不是真的得到,而是使用绑定运算符( >>=
)传递给另一个函数,所以您可以使用用户在程序中进一步input的字符。
Monads的发明者说:用不纯的语言,像tick这样的操作可以用types() – >()的函数表示。 虚假的参数()需要延迟效果,直到函数被应用,并且由于输出types是(),人们可能会猜测函数的目的在于副作用。 相反,在这里,tick的types是M():不需要伪造的参数,M的外观明确指出可能发生什么样的效果 。
我不明白M()如何使空的参数列表(,),更less的虚假,但瓦德很清楚Monad只是表明一种副作用,他们并没有消除它 。 哈斯克尔的追随者似乎欺骗我们自己,当陈述单子消除杂质。
我会让Martin Odersky 回答这个问题
IO monad并不是纯粹的function。 它只是很明显,它是不纯的。
听起来很清楚。