Core language · Values, bindings and identity
Copying and references.
Nift distinguishes a live location from an independent value. Aggregate binding and nested extraction preserve a location; copy() materializes the current value into a recursively independent root.
b := a and b := a[key] keep a live reference to a location. b := copy(a) creates independent data.
Binding another aggregate
$[a := {"x": [1, 2]}]
$[b := a] a ─────┐
├──► root { "x": [1, 2] }
b ─────┘Mutation through either name is visible through the other.
Binding a nested aggregate
$[a := {"x": [[1], [2]]}]
$[b := a.x]
$[c := b[0]] Nift normalizes these as a root binding plus a path. It does not retain a pointer into an array or object.
a ──► root { "x": [[1], [2]] }
│
├── path ["x"] ─────────► b
└── path ["x", 0] ──────► c Locations stay live
$[a := [[1], [2]]]
$[b := a[0]]
$[a.remove(0)]
$[b] @/* [2] — b still means location a[0] */ before remove: after remove:
a ─► [ [1], [2] ] a ─► [ [2] ]
▲ ▲
│ path [0] │ path [0]
b b Prepending has the same consequence: a reference to a[0] follows slot zero. Replacing a[0] likewise changes what the reference sees.
Root rebinding stays live
$[a := [[1]]]
$[b := a[0]]
$[a = [[7]]]
$[b[0]] @/* 7 */ binding slot a binding slot a
│ │
▼ ▼
[[1]] b = a + [0] [[7]] b = a + [0]
▲ ▲
└──────── b resolves ──────────┘ Missing locations fail safely
$[a := [[1]]]
$[b := a[0]]
$[a.clear()]
$[b] @/* error: reference target no longer exists */
$[a.push([3])]
$[b[0]] @/* 3 — location a[0] exists again */ Independent recursive copies
$[a := {"x": [[1], [2]]}]
$[b := copy(a)] a ──► A { "x": X [[1], [2]] }
b ──► B { "x": Y [[1], [2]] }
A ≠ B, X ≠ Y — nested aggregate data is recursively independent $[b.x[0][0] = 99]
$[a.x[0][0]] @/* 1 */ copy() also works on a location reference: Nift resolves the current value and recursively clones it into a new independent root.
Identity versus equality
same(x, y) asks whether aggregate references identify the same location. == asks whether values are structurally equal.
$[a := [[1], [1]]]
$[b := a[0]]
$[c := a[0]]
$[d := a[1]]
$[e := copy(a[0])]
$[same(b, c)] @/* true: same root + path */
$[same(b, d)] @/* false: different path */
$[same(b, e)] @/* false: independent root */
$[b == d] @/* true: structural equality */ The user-facing rule stays small: a nested binding means “this place in this binding,” while copy() means “give me independent data.” It also remains safe across parent growth and reallocation.