【问题标题】:How is immutability practically implemented in the design of Scala applications?在 Scala 应用程序的设计中,不变性是如何实际实现的?
【发布时间】:2011-11-03 19:49:12
【问题描述】:

作为 scala 的新手和当前的 java 开发人员,scala 旨在鼓励在类设计中使用不变性。

这实际上如何转化为类的设计?唯一让我想到的是案例课程。是否强烈鼓励用案例类定义数据?例子? Scala 的类设计中如何鼓励不变性?

作为 Java 开发人员,定义数据的类是可变的。等效的 Scala 类应该定义为案例类吗?

【问题讨论】:

  • 我不明白人们为什么要关闭这个问题。这个问题对我来说很清楚,一点也不主观。
  • 感谢丹尼尔。我希望在其他人也可以分享他们的答案之前,这个问题没有结束。
  • 这里是循环域模型的可能实现:stackoverflow.com/questions/22540385/…

标签: scala


【解决方案1】:

好吧,案例类当然有帮助,但最大的贡献者可能是集合库。默认集合是不可变的,并且这些方法旨在通过生成新集合而不是变异来操作集合。由于不可变集合是持久的,因此不需要复制整个集合,而这是在 Java 中经常要做的事情。

除此之外,for-comprehensions 是 monadic comprehension,它有助于执行不可变任务,还有尾递归优化,这在不可变算法中非常重要,并且在许多库中普遍关注不可变性,例如解析器组合器和 xml。

最后,请注意,您必须请求 var 才能获得一些可变性。参数是不可变的,valvar 一样短。将此与 Java 进行对比,其中参数是可变的,您需要添加 final 关键字以获得不变性。在 Scala 中,保持不可变同样容易或更容易,而在 Java 中,保持可变更容易。

附录

Persistent data structures 是在其修改版本之间共享 部分的数据结构。这可能有点难以理解,所以让我们考虑一下 Scala 的List,它非常基础且易于理解。

Scala List 由两个类组成,称为 consNil。前者其实是用Scala写的::,不过我还是用传统的名字来指代吧。

Nil 是空列表。它不包含任何东西。依赖列表不为空的方法,如headtail 会抛出异常,而其他方法则可以。

当然,cons 必须表示一个非空列表。事实上,cons 正好有两个元素:一个值和一个列表。这些元素被称为 headtail

所以一个包含三个元素的列表由三个 cons 组成,因为每个 cons 将只包含一个值,外加一个 Nil。它必须有一个Nil,因为 cons 必须指向一个列表。由于列表不是循环的,因此 cons 之一必须指向 cons 以外的其他内容。

这样的列表的一个例子是:

val list = 1 :: 2 :: 3 :: Nil

现在,Scala List 的组件是不可变的cons 的值和列表都不能改变。不变性的一个好处是您永远需要在传递集合之前或从其他方法接收集合之后复制它:您知道列表不能更改。

现在,让我们考虑一下如果我修改该列表会发生什么。让我们考虑两个修改:删除第一个元素并添加一个新元素。

我们可以使用tail 方法删除一个元素,其名称绝非巧合。所以,我们写:

val list2 = list.tail

list2 将指向listtail 所指向的同一个列表。什么都没有创建:我们只是重用了list 的一部分。那么,让我们在 list2 前面添加一个元素:

val list3 = 0 :: list2

我们在那里创建了一个新的缺点。这个新的cons 的值(一个head)等于0,它的tail 指向list2。注意listlist3 都指向同一个list2。这些元素被listlist3共享

还有许多其他持久性数据结构。您正在操作的数据是不可变的这一事实使得共享组件变得容易。

您可以在 Chris Okasaki 的著作Purely Functional Data Structures 或他的同名免费提供的thesis 上找到有关此主题的更多信息。

【讨论】:

  • 要么我不理解你的最后一段,要么严重误导。您听起来好像使用 var 而不是 val 会赋予您可变性,而它只允许您重新分配一些变量。
  • @Raphael,重新分配变量是可变性的。任何声明为var 的标识符都是可变的,任何具有var 属性的类都被认为是可变的。
  • Daniel,您能多谈谈 persistent 部分吗?有一些copy-on-change机制,还是别的什么?
  • @Raphael 所有可变性都可以在某处简化为var。如果我有一个val 指向一个可变对象,那么该对象是可变的,因为它有一个var,或者直接或间接地引用了其他具有该对象的对象。事实上,我们可以反过来说:在没有var(和违反规则的反射)的情况下,一切都是不可变的。当然,使用 I/O 可以是不可变的,但仍有副作用。
  • Daniel,我明白你的意思,我理解 Scala 的语义,但它读起来好像 var x 使 x 可变。除此之外,以您的方式解释它也具有误导性,因为您可以拥有带有var 的不可变类。不变性的典型定义只要求对象的视图永远不会改变,而不是它的状态保持不变。这使您(通常)能够在实现不可变类(例如,数据结构)时进行术语共享、惰性评估等。要求所有状态都不可改变比这更强大。
猜你喜欢
  • 1970-01-01
  • 2012-02-17
  • 2019-07-04
  • 2023-03-10
  • 1970-01-01
  • 2018-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多