好吧,案例类当然有帮助,但最大的贡献者可能是集合库。默认集合是不可变的,并且这些方法旨在通过生成新集合而不是变异来操作集合。由于不可变集合是持久的,因此不需要复制整个集合,而这是在 Java 中经常要做的事情。
除此之外,for-comprehensions 是 monadic comprehension,它有助于执行不可变任务,还有尾递归优化,这在不可变算法中非常重要,并且在许多库中普遍关注不可变性,例如解析器组合器和 xml。
最后,请注意,您必须请求 var 才能获得一些可变性。参数是不可变的,val 和var 一样短。将此与 Java 进行对比,其中参数是可变的,您需要添加 final 关键字以获得不变性。在 Scala 中,保持不可变同样容易或更容易,而在 Java 中,保持可变更容易。
附录
Persistent data structures 是在其修改版本之间共享 部分的数据结构。这可能有点难以理解,所以让我们考虑一下 Scala 的List,它非常基础且易于理解。
Scala List 由两个类组成,称为 cons 和 Nil。前者其实是用Scala写的::,不过我还是用传统的名字来指代吧。
Nil 是空列表。它不包含任何东西。依赖列表不为空的方法,如head 和tail 会抛出异常,而其他方法则可以。
当然,cons 必须表示一个非空列表。事实上,cons 正好有两个元素:一个值和一个列表。这些元素被称为 head 和 tail。
所以一个包含三个元素的列表由三个 cons 组成,因为每个 cons 将只包含一个值,外加一个 Nil。它必须有一个Nil,因为 cons 必须指向一个列表。由于列表不是循环的,因此 cons 之一必须指向 cons 以外的其他内容。
这样的列表的一个例子是:
val list = 1 :: 2 :: 3 :: Nil
现在,Scala List 的组件是不可变的。 cons 的值和列表都不能改变。不变性的一个好处是您永远需要在传递集合之前或从其他方法接收集合之后复制它:您知道列表不能更改。
现在,让我们考虑一下如果我修改该列表会发生什么。让我们考虑两个修改:删除第一个元素并添加一个新元素。
我们可以使用tail 方法删除一个元素,其名称绝非巧合。所以,我们写:
val list2 = list.tail
而list2 将指向list 的tail 所指向的同一个列表。什么都没有创建:我们只是重用了list 的一部分。那么,让我们在 list2 前面添加一个元素:
val list3 = 0 :: list2
我们在那里创建了一个新的缺点。这个新的cons 的值(一个head)等于0,它的tail 指向list2。注意list 和list3 都指向同一个list2。这些元素被list 和list3共享。
还有许多其他持久性数据结构。您正在操作的数据是不可变的这一事实使得共享组件变得容易。
您可以在 Chris Okasaki 的著作Purely Functional Data Structures 或他的同名免费提供的thesis 上找到有关此主题的更多信息。