功能性程序设计:副作用实际发生在哪里?
在开始学习Haskell之后,即使在阅读了大量文档之后,我仍然在Haskell中不理解。
据我所知,要执行IO操作,您必须使用“IO monad”以“黑匣子”的形式包装值,以便使用IO monad的任何函数仍然是纯粹的功能。 好吧,很好,但是IO操作实际发生在哪里?
这是否意味着Monad本身并不是纯粹的功能? 或者是IO操作实现了,比方说C语言,并且在Haskell编译器中“嵌入”?
我可以使用纯粹的Haskell写或不写Monad,这可以做IO操作吗? 如果不是,如果语言本身不可能,这种能力从何而来? 如果它在Haskell编译器内嵌入/链接到C代码块,IO Monad是否最终会调用它来执行“脏作业”?
作为前言,尽管有许多写得不好的介绍,但它不是“ IO
Monad
”。 这只是“ IO
类型”。 单子没有什么神奇的。 Haskell的Monad
类是一个非常无聊的东西 - 它只是比大多数语言可以支持的不熟悉和抽象。 即使IO
实现了Alternative
,你也不会看到任何人称IO
“ IO
Alternative
”。 过度关注Monad
只会影响学习。
在纯语言中原则性处理效果(而非副作用!)的概念魔术完全是IO
类型的存在。 这是一个真正的类型。 这不是一个国旗说“这是不纯的!”。 这是一个完整的Haskell类型* -> *
,就像Maybe
或[]
。 输入接受IO值作为参数的签名,如IO a -> IO (Maybe a)
,这是合理的。 输入嵌套IO的签名是有意义的,如IO (IO a)
。
所以如果它是一个真正的类型,它必须有一个具体的含义。 Maybe a
作为一个类型代表类型的可能缺失值a
。 [a]
表示类型为a
0个或更多个值。 IO a
装置的产生类型的值的影响的序列a
。
请注意, IO
的整个目的是表示一系列效果。 正如我上面所说,他们不是副作用。 它们不能隐藏在程序的无伤大雅的叶子中,神秘地改变其他代码背后的东西。 相反,由于它们是一个IO
值,所以效果被明确地调出。 这就是为什么人们试图使用IO
类型来最小化他们程序的一部分。 你在那里做的越少,在远处干扰你的程序的怪异行为的方式越少。
至于你的问题的主要内容,那么 - 一个完整的Haskell程序是一个名为main
的IO
值,以及它使用的定义集合。 编译器在生成代码时,插入一个确实非Haskell代码块,它实际上运行IO
值中的效果序列。 从某种意义上说,这就是Simon Peyton Jones(GHC的长期作者之一)在他的演讲中得到Haskell无用的东西。
确实,无论实际执行IO操作如何,都不能保持概念上的纯粹。 (这里有一个非常不纯的函数来运行在Haskell语言中暴露的IO
操作,我不会再多说它,而是添加它来支持外部函数接口,并且使用它不正确会严重地破坏你的程序。)但是Haskell的重点是为效果系统提供一个原则性的接口,并隐藏无原则的位。 而且它在实践中确实非常有用。
上一篇: Functional programming : Where does the side effect actually happen?