首页 文章

我对可变类的“引用透明度”的理解是否正确?

提问于
浏览
3

从“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")

假设这里的代码是使用xy的完整代码。

我可以说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 回答

  • 2

    它意味着您可能编写包含该表达式的任何可能的程序p。恰当地,这应该根据语言或一组可能的程序来定义。所以你的x在语言中是透明的,在这种语言中唯一有效的程序就是你编写的程序。您的y在 Scala 的子集中是引用透明的,在该子集中您不允许在StringBuilder上调用.append。但这些并不是特别有趣的语言。

    大多数时候,当人们谈论“引用透明”的表达时,他们(含蓄地)表示像Scalazzi 安全的 Scala 子集这样的东西,这通常足以表达(所有?)有用的 Scala 程序,但足够安全,足以推理。因为当然如果你被允许打电话给 e.g. System.identityHashCode(),大多数据称“参考透明”的表达实际上并非如此。

    当然,最重要的定义是操作定义;什么是“引用透明”最终取决于你想用它做什么。一个重要的用例是 compiler/library 优化:我们不希望编译器执行您在示例 1 中给出的替换,因为对于大多数程序,这种“优化”会改变程序的含义。但我们很高兴编译器通过内联不可变常量来优化我们的程序,因为我们的程序“不应该”依赖于特定常量的identityHashCode

  • 1

    据我所知,表达'e'是引用透明的,它是否对所有可能的程序'p'都是透明的。

    对于具体的程序'p',它可以是“参考 transparent-like”,但如果你可以编写另一个违反“替换规则”的程序'px',则表示表达式不是参照透明的。

    import scala.collection.mutable
    
      val b = new mutable.StringBuilder("Hello")
      def e = b.length
    
      def p0 = {
        val l1 = e
        val l2 = e
        l1 + l2
      }
    
      def p1 = {
        val l1 = e
        b.append("World")
        val l2 = e
        l1 + l2
      }
    

    它可能构建违反“e in p 的程序'p'可以用评估 e 的结果代替” - 这意味着'e'不是参考透明的。在可变状态下,构建此程序非常容易。

    在 p0 中我们可以说 e 是参考透明的,但是 p1 很容易打破它。

相关问题