从“scala 中的函数式编程”一书中,我看到了表达式“referential transparent”的定义:
如果对于所有程序 p,p 的所有出现都可以被评估 e 的结果替换而不影响 p 的含义,则表达式 e 是引用透明的。
我有一些代码示例,我不确定它们是否是引用透明的。
我将在可变类的示例中使用scala.collection.mutable.StringBuilder
1.
val x = new StringBuilder("Hello")
println(x.length)
println(x.length)
假设这里的代码是使用x
的完整代码。
我可以说表达式x
是一个参照透明表达式吗?
如果我使用其值new StringBuilder("Hello")
更改所有x
,则不会更改程序的可观察行为:
val x = new StringBuilder("Hello")
println(new StringBuilder("Hello").length)
println(new StringBuilder("Hello").length)
2.
val x = new StringBuilder("Hello")
val y = x.append("aaa")
假设这里的代码是使用x
和y
的完整代码。
我可以说y
是参考透明的,因为它在程序中根本没用过吗?
3.
def getTheClassName(n:Int):String = {
val x = new StringBuilder("hello")
for(int i=0;i<n;i++) {
x.append("world")
}
return x.getClass.getName
}
我可以说x
是参考透明的吗?因为无论我如何用它的值替换它,返回值都不会改变。
PS:也许主要的问题是我不明白for all programs p
是什么意思,它是否意味着现有的完整代码?或者可能添加的任何代码?
2 回答
它意味着您可能编写包含该表达式的任何可能的程序
p
。恰当地,这应该根据语言或一组可能的程序来定义。所以你的x
在语言中是透明的,在这种语言中唯一有效的程序就是你编写的程序。您的y
在 Scala 的子集中是引用透明的,在该子集中您不允许在StringBuilder
上调用.append
。但这些并不是特别有趣的语言。大多数时候,当人们谈论“引用透明”的表达时,他们(含蓄地)表示像Scalazzi 安全的 Scala 子集这样的东西,这通常足以表达(所有?)有用的 Scala 程序,但足够安全,足以推理。因为当然如果你被允许打电话给 e.g.
System.identityHashCode()
,大多数据称“参考透明”的表达实际上并非如此。当然,最重要的定义是操作定义;什么是“引用透明”最终取决于你想用它做什么。一个重要的用例是 compiler/library 优化:我们不希望编译器执行您在示例 1 中给出的替换,因为对于大多数程序,这种“优化”会改变程序的含义。但我们很高兴编译器通过内联不可变常量来优化我们的程序,因为我们的程序“不应该”依赖于特定常量的
identityHashCode
。据我所知,表达'e'是引用透明的,它是否对所有可能的程序'p'都是透明的。
对于具体的程序'p',它可以是“参考 transparent-like”,但如果你可以编写另一个违反“替换规则”的程序'px',则表示表达式不是参照透明的。
它可能构建违反“e in p 的程序'p'可以用评估 e 的结果代替” - 这意味着'e'不是参考透明的。在可变状态下,构建此程序非常容易。
在 p0 中我们可以说 e 是参考透明的,但是 p1 很容易打破它。