jonnifyelixir
F1 · Algebra / F1.03 / F1.04

F1 · Algebra

Immutability & binding

A name is a label fixed to a value, not a box you write into. Values do not change; you build new ones. And in Elixir the equals sign does not assign — it matches.

This is the property the last two modules quietly relied on. Substitution is only valid because a name keeps its value, and composition is only safe because a function cannot reach back and alter its inputs. We separate three ideas that often blur together: binding a name, matching with the equals sign, and the immutability of the data itself.

Names, not boxes

In mathematics, writing x = 3 does not allocate a cell that might later hold something else. It names the value 3 for the rest of the discussion. A binding attaches a name to a value; it does not create a container.

Elixir lets you rebind a name — write x = 1 and later x = 2 — but that is the name pointing at a new value, not the old value changing. Anything that already captured the old value still sees it. Step through the classic case.

bind
attach a name to a value; x = 3 makes x mean 3.
rebind
point a name at a new value; the old value is untouched.
immutable
a value never changes after it is made.
pin
^x matches the current value of x instead of rebinding it.

Binding · rebinding moves the name, not the value

3 / 3
x y [0, 1, 2, 3] new [1, 2, 3] original


      
x = [0, 1, 2, 3] · y = [1, 2, 3] · y is unchanged — rebinding x did not touch it

Algebra

A name denotes a value. Naming something else does not alter what the first name meant.

Elixir

x = [0 | x] rebinds x; a y that aliased the old list still holds it.

Rebinding is not mutation. The value a name held is safe from anything you do to the name afterwards.

Equals is a match

In Elixir = is the match operator, not assignment. It asks whether the left side can be made to match the right, binding any names on the left as a side of that match. A bare name always matches, which is why x = 1 looks like assignment — but a pattern on the left does real work, and a value on the left is a check.

Take x already bound to 1, and try a few matches. The pin ^ says “match against the current value” rather than rebind.

The match operator · given x = 1

1 = x ✓ matches


      
match ✓ · 1 equals the current value of x · nothing new is bound

Algebra

An equation states that two sides are equal; solving it determines the unknowns.

Elixir

= matches and binds; ^x pins; a failed match raises a MatchError.

Reading = as a match, not a store, is the shift that makes the rest of Elixir read clearly — F1.08 turns it into solving by structure.

Data that holds still

Values themselves are immutable. You never change a map or a list in place; an operation that looks like an update returns a new value and leaves the original exactly as it was. Because the new value can share the parts it did not change — the structural sharing of F0.1.3 — this is cheap, not wasteful.

Apply an operation to a small map and watch the original. It does not move.

Immutable update · the original is never touched

original (unchanged) a: 1 b: 2 → updated (new value) a: 1 b: 2 c: 3


      
original = %{a: 1, b: 2} (unchanged) · updated = %{a: 1, b: 2, c: 3} · a and b are shared

Algebra

A value is what it is; producing another value does not revise the first.

Elixir

Map.put/3 returns a new map; the one you passed in is unchanged and safe to share.

Nothing changes underneath a reference you are holding. That is what makes immutable data safe to pass between functions — and, in F0.2, between processes.

What this lands

Three separate things sat behind one word. Binding names a value without a container; the match operator reads = as a test, not a store; and immutability keeps every value fixed once made. Together they are why F1.02 could substitute equals for equals and why F1.03 could compose functions without surprise — nothing a later step does can rewrite an earlier value. The same property makes data safe to share across the processes of F0.2, and the structural sharing of F4 makes it inexpensive. Next, F1.05 puts immutable values into collections and maps a function across them.

Next: F1.05 — Sets, sequences & mappings (planned) — applying one function across a whole collection. The rest of F1 is listed on the chapter page.