scala - 是否有可能强迫对象不变?

时间:2011-12-07 12:06:23

标签: scala functional-programming immutability

我的意思是,如果有一些声明性的方法可以防止对象改变它的任何成员。

在以下示例中

class student(var name:String)

val s = new student("John")

“s”已被宣布为val,所以它总是指向同一个学生。

但有没有办法防止s.name被改为只是声明它就像不可变???

或唯一的解决方案是将所有内容声明为val,并手动强制使用不变性?

3 个答案:

答案 0 :(得分:6)

不,不可能宣布一些不可变的东西。你必须自己强制执行不变性,不允许任何人改变它,这就是删除所有修改类的方法。

有人仍然可以使用反射修改它,但这是另一个故事。

答案 1 :(得分:2)

Scala没有强制执行,因此无法知道。然而,有一个有趣的编译器插件项目名为pusca(我猜它代表Pure-Scala)。 Pure在那里被定义为不改变非局部变量并且没有副作用(例如不打印到控制台) - 因此重复调用纯方法将总是产生相同的结果(所谓的引用透明)。 p>

我自己没有试过这个插件,所以我不能说它是否已经稳定或可用了。

答案 2 :(得分:1)

Scala无法做到这一点。

考虑以下假设的例子:

class Student(var name : String, var course : Course)

def stuff(course : Course) {
    magically_pure_val s = new Student("Fredzilla", course)

    someFunctionOfStudent(s)
    genericHigherOrderFunction(s, someFunctionOfStudent)
    course.someMethod()
}

任何尝试实际实施magically_pure_val关键字的陷阱都是:

  1. someFunctionOfStudent接受任意学生,并未在此编译单元中实现。它是在Student由两个可变字段组成的情况下编写/编译的。我们怎么知道它实际上并没有改变它们?
  2. genericHigherOrderFunction更糟糕;它将采用我们的StudentStudent的函数,但它是多态的。它是否实际变异s取决于它的其他参数是什么;确定在完全通用的编译时需要解决停机问题。
  3. 让我们假设我们可以解决这个问题(也许我们可以设置一些秘密标志,这意味着如果s对象实际上已经变异,异常会被提升,尽管我个人认为不够好。)那个course字段怎么样? course.someMethod()是否会改变它?该方法调用不会直接从s调用。
  4. 更糟糕的是,我们只知道我们已经传递了Course Course的某些子类的实例。因此,即使我们能够分析CourseCourse.someMethod的特定实现并得出结论这是安全的,也可以总是添加Course的新子类,其someMethod的实现}改变Course
  5. 编译器根本无法检查给定对象是否无法变异。 0__提到的pusca插件似乎以与水星相同的方式检测纯度;确保每个方法都知道从其签名纯粹或不纯,并且如果声明为纯粹的任何事物的实现会产生任何可能导致杂质的事情,则通过引发编译器错误(除非程序员承诺)无论如何这种方法都是纯粹的。[1]

    这与简单地声明一个值完全(并且深入)不可变并且期望编译器注意到任何可能触及它的代码是否可以改变它是完全不同的。它也不是一个完美的推理,只是一个保守的推理

    [1] pusca README声称它可以推断出最后一个表达式是对不纯方法的调用的方法的杂质。我不太确定它是如何做到这一点的,因为检查最后一个表达式是否是一个不纯的调用需要检查它是否正在调用一个非声明的不纯方法,该方法应该被此规则声明为不纯,并且实现可能不可用到那时的编译器(实际上可以在以后更改,即使它是)。但我所做的只是看看自述文件并考虑几分钟,所以我可能会遗漏一些东西。