Home Documentation Templates Examples Showcase GitHub
Theme

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.

The short rule.

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 */
Why location semantics?

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.

Declarations & assignments → · Collection operations →