Answers:
因此,按定义,查询数据库或写入文件无法以纯功能样式完成。例如,这就是我们需要monad的原因之一。
没人“需要” monad,那只是描述事物的一种方式。实际上,这可能甚至不是最好的方法。某种形式的效果类型,唯一性类型或基于完全线性逻辑的系统在理论上似乎更具说服力,但与众所周知的类型系统相比则更具根本性,表达起来也更复杂。Haskell中发现的Monadic IO是可用性和简单性之间的折衷,因为它实质上以一种易于与现有语言中已使用的ML样式类型系统共存的方式对完全命令式编程进行建模。
问题是-为什么我们认为STDOUT输出不纯?是的,任何文件处理程序都存在风险-我们永远无法确定总是会写入数据。但是STDOUT呢?为什么我们认为它不可靠?评估本身是否更加不可靠?我的意思是,我们总是可以拉动触发器,从而进行中断计算。
不是,我们也不是。整个程序的输入和输出可以简单地视为自变量,并且可以将整个程序视为一个大的纯函数。如果您从stdin中输入相同的内容,只要它向stdout输出相同的内容,它仍然是一个纯函数。实际上,在引入单例IO之前,Haskell使用基于流的I / O系统,该系统使用纯惰性流进行输入和输出。之所以删除它,是因为使用起来似乎很痛苦,这可能使您对为什么没有听说过这样的事情有所了解。:]
为了以一种愚蠢的方式提出观点,请考虑极简主义的深奥语言Lazy K:
Lazy K是一种垃圾回收的,引用透明的功能性编程语言,具有一个简单的基于流的I / O系统。
Lazy K与其他此类语言的区别在于它几乎完全缺乏其他功能。例如,它不提供集成的Hindley-Milner多态类型系统。它没有附带支持平台无关的GUI编程以及与其他语言的绑定的扩展标准库。也不能编写任何此类库,因为除其他外,Lazy K不提供任何方法来定义或引用内置函数以外的任何函数。缺少对数字,字符串或任何其他数据类型的支持,弥补了这种能力的不足。不过,Lazy K是图灵完备的。
(...)
懒惰的K程序与数学函数生活在永恒的柏拉图领域中,Unlambda页面称其为“纯无类型lambda演算的有福领域”。就像垃圾回收对程序员隐藏了内存管理过程一样,引用透明性也隐藏了评估过程。为了查看Mandelbrot集的图片或为了“运行” Lazy K程序而需要进行一些计算的事实是实现细节。这就是函数式编程的本质。
(...)
如何处理无副作用的语言输入和输出?从某种意义上说,输入和输出不是副作用。可以说,它们是正面和反作用。在懒惰K中也是如此,在该程序中,程序被简单地视为从可能的输入空间到可能的输出空间的函数。
我怀疑您会找到比这更纯粹的功能语言!
但是请记住,以上内容仅适用于从本质上获取纯函数的输入和输出,并通过某种方式将它们“外部”连接到stdin / stdout。那和访问真实的系统级I / O原语之间有很大的区别。除非仔细封装,否则读写流的实施细节可能会泄漏杂质。
我希望这是您不能直接在Haskell中直接执行此操作的主要原因-明智的用例比使用Monadic IO苗条,对于后者而言,访问真实对象有很多好处。我认为,这就是为什么,例如,程序的命令行参数不会简单地作为参数传递给main
,即使在直观上看起来应该如此。
不过,您可以在特定程序中恢复诸如此类的最低版本-只需将参数捕获为纯值,然后将该interact
函数用于程序的其余部分即可。
尽管功能程序的纯度是一个值得追求的目标,但实际上,由于您提到的原因,每个不重要的,有用的程序都会有一些杂质(或“副作用”)。
从定义上说,完全纯净的程序是一个密封的黑匣子,从本质上讲没有意思。
函数语言Haskell通过隔离副作用(例如monad中的输出)来解决此问题。monad保留了纯粹的函数式编程风格,同时仍然可以产生输出。
如果您用“改变外部世界的状态”来考虑纯净度,那么它会有所帮助。这可能包括写入控制台,日志文件,弹出CD或“发射导弹”。
就并发执行而言,这也是一个问题。如果您知道某个函数没有副作用,则可以轻松安排并发,因为您可以证明不存在竞争条件或类似情况。