Skip to content

Value 与 Identity 语义 ​

本文是 Norm 赋值、传参、返回、相等和复制行为的权威规范。对象模型、容器和引用语法都以这里的规则为准。

数据类别 ​

Norm 区分 value 与 identity:

类别类型赋值、传参和返回==
ValueInteger、Boolean、String、enum、用户定义 value、内建容器产生逻辑独立的值结构相等
Identityclass 实例复制对象引用,共享同一对象对象身份相等

统一的 = 复制右侧表达式的值。class 变量保存的值是对象引用,因此复制该值会共享对象;容器保存的是容器值,因此复制后容器结构彼此独立。

norm
Box first = Box(value: 1)
Box second = first
second.value = 2
printLine(first.value)

这里输出 2。

Class 与显式复制 ​

class 实例具有稳定身份。普通赋值、参数传递和函数返回不会隐式创建新对象。

每个 class 都提供 copy():它创建新的顶层对象身份,并逐字段执行普通赋值语义。

norm
Box second = first.copy()

值字段因此逻辑独立;class 字段仍指向原来的嵌套对象。Norm 不提供隐式递归深复制。

容器 ​

Array、List、Map、Set、Stack、Queue、Deque、Pair、Range 与 StringBuilder 是 value。复制容器会复制其结构,并对每个元素执行普通赋值语义。

因此,容器中的 value 元素逻辑独立,class 元素保留对象身份。容器相等与 hash 使用同一套递归规则;作为 Map key 或 Set 元素时仍使用这套语言内建规则。Equatable 与 Hashable 表达显式领域协议,不重载容器的键语义。

索引结果直接用于容器修改时保留容器中的位置,例如 rows[0].add(1) 修改嵌套列表。var row = rows[0] 则按赋值规则复制列表,随后修改 row 不影响 rows;索引结果传参或返回时也遵循同一复制规则。

可执行示例 ​

下面的程序同时展示结构化的值相等、独立的容器结构、共享的 class 身份,以及 copy() 的逐字段行为:

norm
value Position {
  Integer row
  Integer column
}

class Marker {
  String label
  Integer visits = 0

  Void visit() {
    visits = visits + 1
  }
}

class Board {
  Marker marker
  List<Integer> route
}

Void appendStep(List<Integer> route) {
  route.add(9)
}

main() {
  Position start = Position(row: 2, column: 3)
  Position same = Position(row: 2, column: 3)
  printLine(start == same)

  List<Integer> original = [1, 2]
  List<Integer> changed = original
  changed.add(3)
  appendStep(changed)
  printLine(original.size())
  printLine(changed.size())

  Marker marker = Marker(label: "start")
  Board first = Board(marker: marker, route: original)
  Board alias = first
  alias.marker.visit()
  printLine(first == alias)
  printLine(first.marker.visits)

  Board copy = first.copy()
  copy.route.add(4)
  copy.marker.visit()
  printLine(first == copy)
  printLine(first.route.size())
  printLine(copy.route.size())
  printLine(first.marker.visits)
}

经过验收的输出位于源码旁的 norm/tests/docs/language/value_identity.out。

求值和调用 ​

实参表达式严格按源码从左到右求值,参数标签只决定求值结果绑定到哪个形参,不改变求值顺序。

多参数调用必须使用 name: value。裸标识符只有与同位置形参同名时才能作为简写;单参数调用可以省略标签。

norm
merge(left: mergeSort(left), right: mergeSort(right))

实现自由 ​

逻辑独立不要求立即深复制。执行器可以使用写时复制、结构共享、逃逸分析或复制消除,但不能改变身份、相等、修改结果和源码求值顺序。

ref<T> ​

ref<T> 表示 value 存储位置的身份,而不是 class 共享机制。它只接受 value 类型;ref<Class> 不合法,因为 class 已经具有身份。

&location 取得位置引用,*reference 读取位置中的 value,*reference = value 写入位置。复制 ref 保留位置身份,ref 的 == 比较位置而不是内容。完整的可寻址位置与词法生命周期边界见 ref<T> 引用语法。

Norm 0.25