Goblin Memory Model
Goblin doesn't hide how computers work. This page explains what actually happens when your code runs.
Stashes and Addresses
When you write:
x | 5
x | 5
Goblin allocates memory — a stash — somewhere in your system. The number 5 goes inside that stash. The name x becomes a tether: a rope tied to the address of that stash.
Memory:
[0x55555555f2c0]: 5
Tethers:
x -----> 0x55555555f2c0
You can see this yourself:
x | 5 :memaddr(x) /// -> "0x55555555f2c0"
x | 5 :memaddr(x) /// -> "0x55555555f2c0"
The address will be different every time you run your program — the operating system randomizes it for security. But the relationship is always the same: the name is a tether, the tether points to an address, the address is where the value lives.
The name x does not contain 5. It points to a stash that contains 5.
Most Stashes Are Sealed
When you retether a name:
x | 5 x |= 6
x | 5 x |= 6
The stash containing 5 is not modified. Goblin creates a new stash for 6 at a different address and moves the tether.
After x |= 6:
[0x55555555f2c0]: 5 (sealed, abandoned)
[0x55555555f2e0]: 6
x -----> 0x55555555f2e0
Prove it yourself:
x | 5 :memaddr(x) /// -> "0x55555555f2c0" x |= 6 :memaddr(x) /// -> "0x55555555f2e0" (different address)
x | 5 :memaddr(x) /// -> "0x55555555f2c0" x |= 6 :memaddr(x) /// -> "0x55555555f2e0" (different address)
The old stash still exists — sealed, unchanged, abandoned. The garbage collector will reclaim it when nothing is tethered to it anymore.
When you "change" a value in Goblin, you are creating a new stash and moving the tether.
Tethers Point to Addresses, Not to Each Other
x | 5 y | x x |= 6 y /// -> 5
x | 5 y | x x |= 6 y /// -> 5
When you wrote y | x, you tethered y to the address x was pointing to at that moment. When x moved to a new stash, y stayed where it was.
After y | x:
[0x55555555f2c0]: 5
x -----> 0x55555555f2c0
y -----> 0x55555555f2c0
After x |= 6:
[0x55555555f2c0]: 5
[0x55555555f2e0]: 6
x -----> 0x55555555f2e0 (moved)
y -----> 0x55555555f2c0 (unchanged)
Tethers point to addresses, not to other tethers. y | x means "point to wherever x is pointing right now" — not "follow x forever."
Sharing a Stash
When two names point to the same address, they share the same stash:
items | [:a :b :c] backup | items :memaddr(items) /// -> "0x5555555a1040" :memaddr(backup) /// -> "0x5555555a1040" (same)
items | [:a :b :c] backup | items :memaddr(items) /// -> "0x5555555a1040" :memaddr(backup) /// -> "0x5555555a1040" (same)
This is safe in Goblin because stashes are sealed. Neither items nor backup can change what's inside that stash — they can only retether to a different one.
Garbage Collection
When a stash has no tethers left, it becomes abandoned. The garbage collector reclaims it.
Think of it like a shared fridge. If your name isn't on the food, you can't be upset when it disappears.
No tether, no claim. Finders keepers.
x | "sandwich" x |= "pizza" /// The "sandwich" stash is now abandoned. /// GC will reclaim it.
x | "sandwich" x |= "pizza" /// The "sandwich" stash is now abandoned. /// GC will reclaim it.
Why This Matters
This model eliminates an entire class of bugs common in other languages:
original | [:a :b :c] modified | original modified[0] |! :z original /// -> [:a :b :c] (unchanged) modified /// -> [:z :b :c]
original | [:a :b :c] modified | original modified[0] |! :z original /// -> [:a :b :c] (unchanged) modified /// -> [:z :b :c]
Because |! creates a new stash and retethers modified, original is never touched. In Python, this same code would silently corrupt original because Python lists are mutable containers.
Sealed stashes also make concurrency safe by default — multiple threads can read the same stash with no locks because the contents can never change.
The Tether Operators
All four tether operators create stashes and tether to them. They differ in when you use them and what they do:
| Operator | What it does |
|---|---|
\| |
Tether a new name for the first time |
\|= |
Retether an existing name to a new value |
[= |
Shadow tether — temporary override in a nested scope |
\|! |
Update elements inside a collection and retether |
See Tether, Retether, Shadow, and Update for the full treatment of each.
Introspection
Goblin lets you observe the memory model directly:
x | [:a :b :c] :memaddr(x) /// -> memory address of the stash
x | [:a :b :c] :memaddr(x) /// -> memory address of the stash
More introspection tools are Memory Introspection.