TYCHO Search

Search documentation

    ↑ ↓ to move · Enter to open · Esc to close

    Map design notes (maintainers)

    Why the user-facing behavior in reference/maps.md is sound. Users don't need this page.

    Values in a map

    A map [K: V] holds any value type: [string: string], [string: Point], [int: [int]], even [string: [string: int]]. The transpiler generates one concrete map runtime per (key, value) pair the program uses. A program using [string: Point] and [int: [int]] gets two runtimes; one that uses neither pays for neither. No boxing, no tag, no dynamic dispatch.

    Lifetimes follow the same rule as every other heap value:

    fn use(n: int):
        println(str(n))
    
    m := ["a": 1]
    default := 0
    other := 2
    
    v := m.get("a", default)
    m["a"] = other                 # rehashes, moves slots
    use(v)                         # still the old value — v is its own copy
    

    Copying a whole map (b := m) copies every value. == compares entry-wise, independent of insertion order.

    Covers scalars, structs, arrays, and nested maps, uniformly across m[k] = v / m.get / in / delete / keys / len / == / accumulator / inout. Key K is string, int, a newtype over either, a fieldless enum, or a hashable composite. A map is not yet usable as a key.

    Mutating through a map

    m[k] has two roles:

    words := ["ada", "alan", "ada"]
    cnt := []string: int
    for w in words:
        cnt[w] = cnt[w] + 1
    

    It can't dangle: m[k] only appears inside a single mutation target and can't be stored or carried past that statement. Rehash happens before the slot is handed back; push(m[k], v) grows the value buffer, not the table. A key with side effects runs once per statement.

    Lowering: m[k] as a place becomes a slot-pointer accessor that does find-or-insert and returns the slot address. Assignment, index-set, field-set, and push/reserve/pop reuse it, so m[k].field and m[k][i] compose. A scalar read lowers to m.get with the zero default.