如何在Scala中正确表达不可变模型中的引用?

时间:2016-01-30 19:12:07

标签: scala immutability

让我们说我想要一个不可改变的模型,一个世界。人们应该如何建模参考?

case class World(people: Set[Person])

case class Person(name: String, loves: Option[Person])

val alice = Person("Alice", None)
val peter = Person("Peter", Some(alice))
val myWorld = World(Set(alice, peter))

println(myWorld)

输出:

World(Set(Person(Alice,None), Person(Peter,Some(Person(Alice,None)))))

但现在我们有两个名叫爱丽丝的人(在人群和彼得人中)。

在Scala中的不可变模型中处理此引用的最佳做法是什么?

我想通过ID严格引用,但感觉不对。有没有更好的办法? (目前的实现并不支持递归/循环,就像A爱B和B喜欢A.)

3 个答案:

答案 0 :(得分:1)

虽然alice 打印两次,但它只在您的示例中以一个和相同的值存在。通常,如果要跟踪其他不可变对象的变异,您可能会引入一个带有唯一标识符的id字段。但在这里,显然,你只有一个价值。

对于递归引用,请参阅this question。例如,您可以使用按名称参数。

答案 1 :(得分:1)

我认为你必须区分纯粹的价值观,以及具有在状态变化中存活的身份概念的东西。

根据您的模型要求,某人可能属于后一类。例如。如果一个人的年龄发生变化,它仍然是同一个人。

为了识别状态变化的实体,我认为使用某种唯一标识符没有任何问题。根据您的模型,在模型的顶层创建一个从人员ID到人员状态的地图,然后在人员状态或单独的数据结构中表达人与人之间的关系可能是个好主意。

这样的事情:

case class Person(name: String, age: Int, loves: Set[PersonRef])

case class PersonRef(id: Long) // typesafe identifier for a person

case class World(persons: Map[PersonRef, Person])

请注意,人员状态不包含ID,因为具有不同ID的两个人可能具有相同的状态。

这种方法的一个问题是世界可能处于不一致的状态。例如。有人可以爱一个世界上不存在的人。但我真的没有办法解决这个问题。

我认为可能值得查看面临类似问题的scala库。例如。

diode的概念为a reference to a value elsewhere in the model

scala graph允许定义自定义节点和边缘类型。

答案 2 :(得分:1)

使用不可变数据结构对某些应用程序域进行建模时,不应使用对象标识来依赖任何东西。试想一下如何更新不可变模型:你会生成一个具有不同身份的修改后的副本,即使它代表相同的东西"。您如何确保将模型中的所有参考文献设置为新的修改后的副本?

因此,你必须明确表达身份:问问自己某事物的身份是什么,例如:唯一ID或一组唯一属性,例如对于人名,出生日期和地点等。 (但要小心,虽然一个人的真实出生日期永远不会改变,但是存储在你的日期模型中的那个可能是因为你的数据集中的错误)。

然后使用此信息指向其他任何地方的对象。 明确身份需要您在构建数据模型时考虑它。这可能感觉像是额外的负担,但实际上它可以避免以后遇到很多麻烦。 例如,序列化将很容易,分发将很容易,添加某种版本支持将很容易等等。

作为一项规则,您应该只使用对相同信息的引用,如果这些信息巧合的话。如果你指的是同一个"身份"并且两个地方的信息必须相同,使用一些明确的ID。