# Maps Maps are Deft's persistent hash maps, written `%{ :key value ... }`, and tagged maps (`%Point{ :x 1 }`) are the same structure carrying a type name. Every operation here returns a new map and leaves the original untouched. These bare functions are compiled into every runtime and need no import. For the broader collection model and destructuring patterns, see Collections and Pattern Matching. ## Functions | Function | Purpose | |---|---| | `keys m` | Keys as a list | | `values m` | Values as a list | | `entries m` | `@{ key value }` pairs as a list | | `merge m1 m2` | Merge two maps; later keys win | | `get m key ?default` | Lookup by key (maps) or index (lists) | | `assoc m k v ...` | Add or overwrite one or more keys | | `dissoc m k` | Remove a key | | `tag m type-name` | Wrap a plain map as a tagged instance | | `untag m` | Strip the tag, returning a plain map | | `assoc-in m path v` | Set a value at a nested path | | `get-in m path ?default` | Look up a value at a nested path | `get` auto-converts string keys to keywords, so `[get $m "name"]` and `[get $m :name]` agree. `merge` takes exactly two maps — apply it again to combine more. `assoc` accepts any number of key-value pairs after the map, so one call can set several keys. `keys`, `values`, and `entries` return in insertion order, which is deterministic for a given sequence of operations but not sorted. `entries` is the inverse of `from-entries`:
[from-entries [entries $m]]                 # round-trips back to $m
Paths for `assoc-in` and `get-in` are vectors of keys read from outermost to innermost. `assoc-in` requires each intermediate level to already be a map; `get-in` returns the default (`nil` when omitted) as soon as a level is missing. `tag` takes a string type name and produces `%Point{ ... }`; `[type [tag $m "Point"]]` reports `"Point"`. `untag` returns the inner plain map; pattern matching against tagged maps destructures both the tag and the fields (see Pattern Matching). ## Examples
def user %{ :name "Alice" :age 30 :role :admin }

$user~name                                  # => "Alice" — field access
[get $user :name]                           # => "Alice"
[get $user :email "n/a"]                    # => "n/a" — default
$user                                       # the original is unchanged
Adding, updating, and removing keys:
[assoc $user :email "alice@example.com"]    # add a key
[assoc $user :age 31 :role :owner]          # several pairs at once
[dissoc $user :role]                        # remove a key
[merge $user %{ :age 31 :city "Paris" }]    # later map wins
Iteration helpers return lists:
[keys $user]                                # => @{ :name :age :role }
[values $user]                              # => @{ "Alice" 30 :admin }
[entries $user]                             # => @{ @{:name "Alice"} ... }
Nested access and update:
def cfg %{ :db %{ :host "localhost" :port 5432 } }

[get-in $cfg @{ :db :host }]                # => "localhost"
[get-in $cfg @{ :db :user } "root"]         # => "root" — default
[assoc-in $cfg @{ :db :port } 6543]         # => port set to 6543
[assoc-in %{ :a %{} } @{ :a :b } 1]         # => %{ :a %{ :b 1 } }
Tagged maps carry a type name and behave like their plain version:
def point [tag %{ :x 10 :y 20 } "Point"]

[type $point]                               # => "Point"
$point~x                                    # => 10
[untag $point]                              # => %{ :x 10 :y 20 }
`assoc`, `dissoc`, and `merge` preserve a tag when given one: `[assoc $point :z 30]` is still a `%Point`. ## See Also - Collections — literals, persistence, `from-entries` - Pattern Matching — tagged-map destructuring - Lists — `zip`, `from-entries`, positional access - Sets — the set-shaped collection