Scala 中方法副作用的含义

Meaning of method side effects in Scala

Martin Odersky 的书 'Programming in Scala' 谈到避免使用有副作用的方法。 比如,下面的方法有写入标准输出流的副作用。

def printArgs(args: Array[String]): Unit = {
  args.foreach(println)
}

然后说,更好的方法是定义一个格式化的方法 用于打印的传递参数,但只是 returns 格式化字符串,如:

def formatArgs(args: Array[String]) = args.mkString("\n")

我不明白这两种方法有何不同conceptually.Afterall,我们的目的是打印一个string.If我们不在目标方法中这样做,然后客户端代码会这样做,这意味着我们只是将导致 'side effects' 的代码从一个地方转移到另一个地方。

它们是不同的,因为副作用显然包含在系统最边缘的少数方法中。

副作用会破坏许多 理想的属性,例如可组合性、可测试性、可维护性、可重用性、局部推理、引用透明性、纯度、等式推理等等。因此,您希望将它们包含在尽可能小的一段代码中。

仅举一个关于可组合性的例子。如果除了将参数打印到控制台外,您还想将它们写入日志文件怎么办?注意这两者其实是完全一样的东西,你只是在写入一个IO流,其中一个恰好连接到一个文件,其中一个恰好连接到终端的标准输出。

然而,您不能重复使用该逻辑,您必须复制它,因为生成字符串的逻辑与打印它的逻辑混合在一起。而在第二个解决方案中,如果您有一个方法,例如,将字符串记录到数据库中,那么您可以简单地将该方法与从参数生成字符串的方法组合起来,以便有一个方法将您的参数记录到数据库中.

关于可测试性的另一个例子:打印到终端的测试方法真的很痛苦。您必须以某种方式捕获终端的输出。

测试 return 字符串的方法很简单,您只需将 return 值与预期值进行比较即可。而且您实际上 没有 来测试打印到终端,因为这是 Scala 标准库提供的一种方法,该方法已经过广泛测试(如果它被破坏,反正你会有更大的问题)。

请注意,在第一个解决方案中,您被迫做重复的工作来测试println是否有效,没有办法解决它。