Assignment through fields & indexes

Assignment reaches into structures. The root binding must be mutable.

mut user = { name: "Alice", tags: ["a"] }

user.name = "Bob"
user.tags[0] = "admin"
user.profile.city = "Cairo"     # nested paths work

Why the root must be mutable

let user = { name: "Alice" }
user.name = "Bob"               # error

Ecko collections have value semantics: a map is a value, not a reference to one. So mutating a field is conceptually replacing the whole value bound to the name - which is exactly what let forbids. The rule falls out of the value model rather than being a separate restriction to remember.

Value semantics, briefly

mut a = [1, 2, 3]
b = a
a[0] = 99
print(b[0])       # 1 - b is an independent value

Binding does not alias. Underneath, collections are reference-counted and copied only when a shared one is written to, so this costs nothing to pass around and copies only when it must. The observable behaviour is plain value semantics.

The exception is cell, which shares deliberately.

Bounds and keys are strict

mut xs = [1, 2, 3]
xs[9] = 0         # error: index 9 is out of bounds (len 3)

Assignment does not grow a list, and it does not create a missing intermediate map. Use push to extend, and insert to add a key:

xs = push(xs, 4)
m  = insert(m, "key", value)

Both return a new value rather than mutating in place, which is the functional idiom and composes with pipelines. Path assignment is the imperative shorthand for when that reads better.