如何在 Scala 的不可变模型中正确表达引用?
How to properly express references in immutable model in Scala?
假设我想要一个不变的模型,一个世界。应该如何建模引用?
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)))))
但是现在我们有两个独立的人叫 Alice(在 people set 和 peter person 中)。
在 Scala 的不可变模型中处理此引用的最佳实践是什么?
我想过通过id严格引用,但是感觉不太对。有没有更好的办法? (目前的实现也不支持 recursion/circle 就像 A 爱 B 和 B 爱 A。)
尽管 alice
被 打印 两次,但在您的示例中它仅作为 一个相同的 值存在。一般来说,如果您想跟踪一个不可变对象的变化,您可能会引入一个带有唯一标识符的 id
字段。但是在这里,很明显,你只有一个值。
有关递归引用,请参阅 this question。例如,您可以使用别名参数。
我认为您必须区分纯值和具有随状态变化而存在的身份概念的事物。
一个人可能属于后一类,这取决于您的模型的要求。例如。如果一个人的年龄改变了,那还是同一个人。
为了通过状态变化识别实体,我认为使用某种唯一标识符没有任何问题。根据您的模型,在模型的顶层建立一个从人员 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 has the concept of a reference to a value elsewhere in the model
scala graph 允许定义自定义节点和边类型。
在使用不可变数据结构对某些应用程序域进行建模时,不应使用对象标识来依赖任何东西。想想你将如何更新一个不可变模型:你将生成一个具有不同身份的修改副本,即使它代表相同的 "thing"。您如何确保已将模型中的所有引用都设置为新的修改后的副本?
因此,您必须使身份明确:问问自己某物的身份是什么,例如一个唯一的 ID 或一组唯一的属性,例如对于人名、出生日期和地点等。 (不过要小心,虽然一个人的真实出生日期永远不会改变,但存储在您的日期模型中的日期可能是因为您的数据集中存在错误)。
然后使用此信息从其他地方指向一个对象。
明确身份要求您在构建数据模型时考虑这一点。这可能感觉像是一个额外的负担,但实际上它避免了以后的很多麻烦。
例如,序列化会很容易,分发会很容易,添加某种版本控制支持会很容易等等。
通常情况下,您应该只引用相同的信息,前提是这些信息只是巧合。如果您指向相同的 "identity" 并且两个地方的信息必须相同,请使用一些显式 ID。
假设我想要一个不变的模型,一个世界。应该如何建模引用?
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)))))
但是现在我们有两个独立的人叫 Alice(在 people set 和 peter person 中)。
在 Scala 的不可变模型中处理此引用的最佳实践是什么?
我想过通过id严格引用,但是感觉不太对。有没有更好的办法? (目前的实现也不支持 recursion/circle 就像 A 爱 B 和 B 爱 A。)
尽管 alice
被 打印 两次,但在您的示例中它仅作为 一个相同的 值存在。一般来说,如果您想跟踪一个不可变对象的变化,您可能会引入一个带有唯一标识符的 id
字段。但是在这里,很明显,你只有一个值。
有关递归引用,请参阅 this question。例如,您可以使用别名参数。
我认为您必须区分纯值和具有随状态变化而存在的身份概念的事物。
一个人可能属于后一类,这取决于您的模型的要求。例如。如果一个人的年龄改变了,那还是同一个人。
为了通过状态变化识别实体,我认为使用某种唯一标识符没有任何问题。根据您的模型,在模型的顶层建立一个从人员 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 has the concept of a reference to a value elsewhere in the model
scala graph 允许定义自定义节点和边类型。
在使用不可变数据结构对某些应用程序域进行建模时,不应使用对象标识来依赖任何东西。想想你将如何更新一个不可变模型:你将生成一个具有不同身份的修改副本,即使它代表相同的 "thing"。您如何确保已将模型中的所有引用都设置为新的修改后的副本?
因此,您必须使身份明确:问问自己某物的身份是什么,例如一个唯一的 ID 或一组唯一的属性,例如对于人名、出生日期和地点等。 (不过要小心,虽然一个人的真实出生日期永远不会改变,但存储在您的日期模型中的日期可能是因为您的数据集中存在错误)。
然后使用此信息从其他地方指向一个对象。 明确身份要求您在构建数据模型时考虑这一点。这可能感觉像是一个额外的负担,但实际上它避免了以后的很多麻烦。 例如,序列化会很容易,分发会很容易,添加某种版本控制支持会很容易等等。
通常情况下,您应该只引用相同的信息,前提是这些信息只是巧合。如果您指向相同的 "identity" 并且两个地方的信息必须相同,请使用一些显式 ID。