为什么未来有副作用?

时间:2017-05-26 07:33:48

标签: scala functional-programming

我正在阅读这本书FPiS,并在第107页上写道:

  

我们应该注意到Future没有纯粹的功能界面。   这是我们不希望我们的库用户的原因之一   直接处理Future。但重要的是,即使方法上   未来依赖副作用,我们整个Par API仍然纯净。它的   只有在用户调用run并且实现接收到之后   我们公开Future机器的ExecutorService。我们的用户   因此编程到一个纯接口的实现   然而,在一天结束时依赖于效果。但既然我们的   API仍然是纯净的,这些影响不是副作用。

为什么Future还没有纯粹的功能界面?

3 个答案:

答案 0 :(得分:17)

问题在于,由于未来的热切性质,创造一个引起副作用的未来本身也是副作用。

这打破了引用透明度。即如果您创建一个仅打印到控制台的Future,将来会立即运行并运行副作用而不需要它。

一个例子:

for {
  x <- Future { println("Foo") }
  y <- Future { println("Foo") }
} yield ()

这导致&#34; Foo&#34;被打印两次。现在,如果Future是引用透明的,我们应该能够在下面的非内联版本中获得相同的结果:

val printFuture = Future { println("Foo") }

for {
  x <- printFuture
  y <- printFuture
} yield ()

然而,这反过来打印&#34; Foo&#34;只有一次甚至更多的问题,无论你是否包括for-expression,它都会打印出来。

使用引用透明表达式,我们应该能够在不改变程序语义的情况下内联任何表达式,Future不能保证这一点,因此它会破坏引用透明性并且本质上是有效的。

答案 1 :(得分:9)

FP的基本前提是referential transparency。换句话说,避免副作用。

副作用是什么?来自Wikipedia

  

在计算机科学中,如果函数或表达式修改了其范围之外的某个状态,或者与其调用函数或外部世界具有可观察的交互,则称其具有副作用。 (除非按惯例,返回一个值:返回一个值会对调用函数产生影响,但这通常不被视为副作用。)

什么是Scala未来?来自documentation page

  

Future是一个可能尚不存在的值的占位符对象。

因此,未来可以从尚未存在的价值转变为现有价值,而无需与程序的其他部分进行任何交互,并且正如您所引用的那样:&#34; Future上的方法依赖于效果&#34;

Scala期货似乎没有保持参考透明度。

答案 2 :(得分:5)

据我所知,Future在创建时会自动运行计算。即使它在嵌套计算中没有副作用,它仍会打破flatMap组合规则,因为它随时间改变状态:

someFuture.flatMap(Future(_)) == someFuture // can be false

除了平等实施问题,我们可以在这里遇到竞争条件:新Future会立即运行一小段时间,而isCompleted可能与someFuture不同,如果它已经完成。

为了纯洁。它代表的效果,Future应该推迟计算并仅在明确要求时运行它,例如Par(或scalaz&#39; s Task )。