Scala 不变性和共享引用-如何协调?

Scala 不变性和共享引用-如何协调?,scala,immutability,Scala,Immutability,考虑这个简化的应用程序域: 刑事调查数据库 人员是否有人参与调查 报告是调查的一部分 报告引用了一个主要的人(调查对象) 一份报告的共犯与其他调查或报告有次要关系(当然可能是主要的) 这些类具有用于将其存储在数据库中的ID,因为它们的信息会随着时间的推移而变化(例如,我们可能会为一个人找到新的别名,或者将感兴趣的人添加到报告中) 如果这些存储在某种数据库中,并且我希望使用不可变对象,那么似乎存在状态和引用方面的问题 假设我更改了一些关于人的元数据。由于我的人对象是不可变的,我可能会有一些代

考虑这个简化的应用程序域:

  • 刑事调查数据库
  • 人员
    是否有人参与调查
  • 报告
    是调查的一部分
  • 报告
    引用了一个主要的
    (调查对象)
  • 一份
    报告
    的共犯与其他调查或报告有次要关系(当然可能是主要的)
  • 这些类具有用于将其存储在数据库中的ID,因为它们的信息会随着时间的推移而变化(例如,我们可能会为一个人找到新的别名,或者将感兴趣的人添加到报告中)

如果这些存储在某种数据库中,并且我希望使用不可变对象,那么似乎存在状态和引用方面的问题

假设我更改了一些关于
的元数据。由于我的
对象是不可变的,我可能会有一些代码,如:

class Person(
    val id:UUID,
    val aliases:List[String],
    val reports:List[Report]) {

  def addAlias(name:String) = new Person(id,name :: aliases,reports)
}
因此,具有新别名的my
Person
将成为一个新对象,也是不可变的。如果
报表
引用了该人员,但别名在系统中的其他位置发生了更改,则my
报表
现在将引用“旧”人员,即没有新别名的人员

同样,我可能会:

class Report(val id:UUID, val content:String) {
  /** Adding more info to our report */
  def updateContent(newContent:String) = new Report(id,newContent)
}
因为这些对象不知道谁引用了它们,所以我不清楚如何让所有的“引用者”知道有一个新的对象可以代表最近的状态

这可以通过让所有对象从中央数据存储“刷新”以及创建新的、更新的对象存储的所有操作到中央数据存储来实现,但这感觉像是对底层语言引用的一次笨拙的重新实现。也就是说,只需将这些“辅助可存储对象”变得更清晰可变。因此,如果我向
人员添加别名,所有引用者都会看到新值,而不做任何操作


当我们想要避免可变性,或者这是一种不变性没有帮助的情况时,如何处理这个问题?

如果X指的是Y,两者都是不可变的,并且Y发生了变化(即,您用更新的副本替换它),那么您别无选择,只能同时替换X(因为它发生了变化,因为新的X指向新的Y,而不是旧的Y)

在高度互联的数据结构中,这很快成为一个令人头疼的问题

  • 忘记一般的不变性。使链接可变。根据需要修复它们。确保确实修复了它们,否则可能会出现内存泄漏(X表示旧Y,它表示旧X,它表示旧Y,等等)
  • 不存储直接链接,而是存储可以查找的ID代码(例如,哈希映射中的键)。然后需要处理查找失败的情况,但其他情况都非常可靠。当然,这比直接链接慢一点
  • 改变整个世界。如果某个东西被改变了,链接到它的所有东西也必须被改变(在复杂的数据集中同时执行此操作是很棘手的,但理论上是可能的,或者至少它的可变方面可以被隐藏,例如,使用大量惰性VAL)

我想这取决于你的查找和更新速度。

我认为你是在试图打破这个圈子。人是不可变的,关于人的报告列表是人的一部分,报告列表可以更改


一个不可变的人有可能引用一个可变的PersonRecord来保存诸如报告和别名之类的东西吗?

我建议你阅读他们如何在和Akka中处理这个问题。了解一下。还有我的一些想法

不变性不是为了自身而存在的。不变性是抽象的。它不“存在”在自然界中。世界是可变的,世界是永久变化的。所以数据结构是可变的是很自然的-它们描述了给定时刻真实或模拟对象的状态。这里看起来像OOP rulez。在概念层面上,这种态度的问题是RAM中的对象!=真实对象-数据可能不准确吃,它会带来延迟等

因此,在大多数琐碎需求的情况下,您可以处理所有可变的内容—人员、报告等—在以下情况下会出现实际问题:

  • 从并发线程修改数据结构
  • 用户为相同的对象提供约束更改
  • 用户提供的数据无效,应回滚该数据

  • 使用简单易变的模型,您将很快得到不一致的数据和破碎的系统。易变性容易出错,不可能实现不变。您需要的是事务世界视图。在事务程序中可以看到不可变的世界。STM管理更改,以一致且线程安全的方式应用。

    在大多数情况下,second选项是最好的(使用ID作为链接,而不是地址)选项2不是通过对象引用复制语言运行库已经在做的事情吗?这有什么好处?语言运行库可能比我更好地实现引用管理。@dave:不,这是一种根本不同的思考问题的方式。一种方式是,“X有一个Y在那里”另一个是,“X有一个我希望是Y的东西的名字”。不变性说,“无论Z现在有什么,Z永远都有。”如果Y是不可变的,并且被替换,那么X就不再有正确的名字了。但是X仍然有它的名字,也许其他东西现在有这个名字了(你想要的就是那个同名的东西)。这将解耦对X和Y的更新,如果您实际上有许多不同的东西,它们都互相使用,这将非常有用。那么,这样做比允许运行时为我做有什么好处?通过让我的持久化对象可变,我基本上可以免费获得所有这些。+1:option#2似乎效果最好。循环依赖CI我们不可能做到这一点