函数式编程中的副作用问题

20
在一本函数式编程的书中,作者提到以下是副作用:
  1. 修改变量
  2. 就地修改数据结构
  3. 在对象上设置字段
  4. 抛出异常或停止错误
  5. 打印到控制台或读取用户输入
  6. 读取或写入文件
  7. 在屏幕上绘制
我想知道如果它们都是副作用,如何编写纯函数式程序而不读取或写入文件? 如果可以,函数世界中实现这个的常见方法是什么?
谢谢, Mohamed

4
我认为这是不可能的。我们总是在编写程序时考虑副作用,目标并不是完全避免它们,因为如果没有副作用,程序就什么也不做 :)目标是尽可能编写更多纯净的代码,并将其与不纯的代码分离。 - cstuncsik
3
可能是与“函数式语言如何建模副作用?”(https://dev59.com/BW865IYBdhLWcg3wYNZS)重复的问题。 - sepp2k
1
继续 @cstuncsik 的思路,我们这样做的部分原因是将不纯净的代码分离出来,这部分是我们不关心测试的 - 它不是“我们正在编写的”。如果我们使所有其余部分(即“我们正在编写的”)都变得纯净,那么它就是可预测/可测试的,并且不纯净的表面被最小化。 - jinglesthula
2个回答

10
适当回答这个问题可能需要一本书(不太长)。这里的重点是函数式编程旨在将逻辑描述/表示与其实际运行时解释分离。您的函数式代码仅表示(不运行)程序效果作为值,将返回某种抽象语法树来描述计算。代码的另一部分(通常称为解释器)将获取这些值并惰性地运行实际效果。那部分不是函数式的。

如何编写一份纯函数式程序,使其在任何情况下都有用?这是不可能的。纯函数式程序只会让 CPU 加热,它需要一个不纯的部分(解释器),才能真正将数据写入磁盘或网络中。以这种方式进行编程有几个重要的优点。纯函数式部分易于测试(测试纯函数很容易),而纯函数的引用透明性使得在本地推理代码变得容易,整个开发过程更少出现错误,更加高效。同时,它还提供了优雅的处理传统上混乱防御性代码的方式

那么,在函数式编程中实现副作用的常见方法是什么呢?就像前面所说的,使用值来表示它们,然后编写解释这些值的代码。关于整个过程的非常好的解释可以在这个博客系列中找到。


4

为了简洁起见,让我过度简化并简短地说:

在纯函数式编程中处理“副作用”,您(程序员)从输入到输出编写纯函数,系统通过将这些纯函数应用于“真实世界”来引起副作用。

例如,要读取整数x并写入x+1,您(粗略地说)编写一个函数f(x) = x+1,系统将其应用于实际输入并输出其返回值。

另一个例子是,您的纯函数不会引发异常作为副作用,而是返回表示异常的特殊值。 Haskell中的各种“单子”(如IO)概括了这些思想,即通过纯函数表示副作用(当然,实际的实现更加复杂)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接