我正在阅读这本书FPiS,并在第107页上写道:
我们应该注意到Future没有纯粹的功能界面。 这是我们不希望我们的库用户的原因之一 直接处理Future。但重要的是,即使方法上 未来依赖副作用,我们整个Par API仍然纯净。它的 只有在用户调用run并且实现接收到之后 我们公开Future机器的ExecutorService。我们的用户 因此编程到一个纯接口的实现 然而,在一天结束时依赖于效果。但既然我们的 API仍然是纯净的,这些影响不是副作用。
为什么Future还没有纯粹的功能界面?
答案 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
)。