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.