Tzopilotl
Docs
GitHub

Tzopilotl by Example

14. Maps

14.1 Map Literals

-- Create a map with [key: value] syntax
let ages = ["Alice": 30, "Bob": 25, "Carol": 35];

-- Map type annotation: [K:V]
let scores [String:Int] = ["math": 95, "science": 88];

The empty map [:] names no key or value type, so it takes one from whatever surrounds it: an annotation, the parameter it is passed to, the struct field it fills, or the literal it sits inside — and within a literal, from a sibling element when there is no annotation. The other empty collection literals ([], Set(), List(), #[], #[:]) work the same way.

let empty [Int:Int] = [:];      -- from the annotation

fn tally(m [Int:Int]) Int { m length }
[:] tally println;                -- 0    (from the parameter)
tally([:]) println;               -- 0

struct Counts { m [Int:Int] }
Counts { m: [:] } println;        -- from the field's declared type

[[1: 2], [:]] println;           -- from the sibling entry: [[1: 2], [:]]

With several overloads in play, the ones that fit the other arguments decide. If those still disagree about this parameter — or nothing in the context pins the type down, as in [[], []] — annotate instead.

14.2 Accessing Values

let m = ["a": 1, "b": 2, "c": 3];

-- Subscript returns Option<V> (safe for missing keys)
m["b"] println;               -- Option<Int>.some(2)
m["z"] println;               -- Option<Int>.none

-- Use unwrap to extract the value (halts on none)
m["b"] unwrap println;        -- 2

-- Use unwrapOr to provide a default
m["z"] unwrapOr(99) println; -- 99

-- get(map, key) also returns Option<V>
m get("b") unwrap println;    -- 2

-- get(map, key, default) returns V directly
m get("z", 99) println;       -- 99

-- contains(map, key)
m contains("a") println;      -- true
m contains("z") println;      -- false

14.3 Modifying Maps

-- Indexed assignment: insert-or-update in place
var m = ["a": 1];
m["b"] = 2;                     -- insert
m["a"] = 99;                    -- update existing
m length println;                -- 2
m["a"] unwrap println;        -- 99

-- put returns a NEW map and leaves m unchanged
let m2 = m put("c", 3);
m2 length println;               -- 3
m length println;                -- 2  (untouched)

-- remove returns a new map without the key
let m3 = m2 remove("a");
m3 contains("a") println;     -- false

-- merge combines two maps (second wins on conflicts)
let a = ["x": 1, "y": 2];
let b = ["y": 99, "z": 3];
merge(a, b) println;             -- [x: 1, y: 99, z: 3]

-- copy: maps are passed by reference; copy when an alias must be independent
var p = ["k": 1];
var q = p copy;
p["k"] = 2;
p["k"] unwrap println;        -- 2
q["k"] unwrap println;        -- 1  (independent)

14.4 Inspecting Maps

let m = ["x": 10, "y": 20];

m length println;                -- 2
m keys println;                  -- ["x", "y"]
m values println;                -- [10, 20]

Mutability

Maps are heap-allocated and passed by reference. The functions put, remove, and merge build and return new maps without touching their inputs. Indexed assignment m[k] = v writes through to this map in place — including through any alias that points at the same map. Use copy to give an alias independent state.