No matching definitions.

tur/map

stdlib/map.tur

typed Map[K V]: persistent hash map requiring Hash[K] and MapKey[K].

Backed by the persistent HAMT from tur/hamt.

Since: Phase TM0 (typed surface: Phase TMS2)

defstruct

Map

(defstruct Map :heap [K V] (hamt :ptr<void>))

parameterized persistent map with key type K and value type V.

Map[K V] is a non-transparent :heap struct holding a single ptr<void> to its backing HAMT. The field is never accessed structurally (every operation is inline-C or delegates to one); it monomorphizes to a real Map__K__V so typed consumers receive a (Map__K__V *) by value.

Since: Phase TM0

defn

map-new

(map-new [K V])

create a new empty typed map.

map-new is generic and nullary, so its K/V cannot be inferred from arguments. Annotate it at the binding site when the type is otherwise unconstrained:

(let [m (:: (map-new) (Map cstr int))] ...)

In a function whose declared return type fixes K/V, a bare (map-new) tail is enough (the return annotation pins K/V) -- see map-empty-for.

An empty Map[K V].

(:: (map-new) (Map int int))  ; => empty Map[int int]

Since: Phase TM0

defn

map-empty-for

(map-empty-for [^Hash K ^MapKey K V])

internal: an empty Map[K V] whose K/V are pinned by a sample key/value pair. Used by the hamt-of / #map{...} literal lowering, where the key/value types are known from the first literal pair but cannot be written as an annotation by the expanding macro. The k/v arguments are consumed only for type inference; the returned map is empty.

Since: Phase TMS3

defn

map-hamt

(map-hamt [K V])

internal accessor for the HAMT backing store.

defn

map-wrap

(map-wrap [K V])

internal constructor from a HAMT pointer.

defn

map-assoc-eq-o

(map-assoc-eq-o [K V])

============================================================================ Raw runtime bridges (#fx{Unsafe}-style, internal).

These carry the key already boxed into the int64 carrier (a :int, not :K) and an explicit comparator/ownership flag. They are the thin C layer the public Hash/MapKey-dispatched accessors below delegate to. The handle is typed (Map K V) so the public accessors thread K/V through them, but the carrier key is always :int. ============================================================================ map-assoc-eq-o -- raw insert/update: explicit hash, boxed carrier key, comparator and ownership flag.

defn

map-get-eq-o

(map-get-eq-o [K V])

raw lookup: explicit hash, boxed carrier key, comparator.

defn

map-has-eq-o?

(map-has-eq-o? [K V])

raw existence test: explicit hash, boxed carrier key.

defn

map-dissoc-eq-o

(map-dissoc-eq-o [K V])

raw removal: explicit hash, boxed carrier key, comparator.

defn

map-assoc-eq

(map-assoc-eq [K V])

---------------------------------------------------------------------------- Explicit hash + key-equality content API (TCE4).

Like the -eq-o family but with the key passed RAW (as :K, not a boxed carrier) and no ownership flag (owned == 0). These give direct control of the hash word, which is what a forced-collision test needs (two distinct keys sharing hash 0 stay distinct under the comparator). The public map-assoc/map-get accessors are preferred for ordinary use. ---------------------------------------------------------------------------- map-assoc-eq -- insert/update with an explicit hash and key-equality fn.

msource Map[K V]
hprecomputed content hash of key
keykey of type K
valvalue of type V
keyeqa captureless (c-fn [K K] bool) used to compare keys that share a
hash. Must carry no environment (it is called as a bare C function
pointer); pass a top-level defn or a captureless lambda, or a
MapKey carrier comparator like tur-int-carrier-eq? /
tur-cstr-key-eq?. A capturing closure is rejected at compile time.

A new Map[K V] with the entry inserted or updated. Keys that are equal under keyeq (not just identical pointers) collapse to one entry.

(map-assoc-eq m (cstr-hash k) k v tur-cstr-key-eq?)

Since: TCE4

defn

map-get-eq

(map-get-eq [K V])

look up a key using an explicit hash and key-equality fn.

mMap[K V]
hprecomputed content hash of key
keykey of type K
keyeqkey comparison fn (see map-assoc-eq)

The value of type V, or 0 (none) if absent.

(map-get-eq m (cstr-hash k) k tur-cstr-key-eq?)

Since: TCE4

defn

map-has-eq?

(map-has-eq? [K V])

key-existence test using an explicit hash and equality fn.

mMap[K V]
hprecomputed content hash of key
keykey of type K
keyeqkey comparison fn (see map-assoc-eq)

true if the key is present under keyeq, false otherwise.

(map-has-eq? m (cstr-hash k) k tur-cstr-key-eq?)  ; => true or false

Since: TCE4

defn

map-dissoc-eq

(map-dissoc-eq [K V])

remove a key using an explicit hash and equality fn.

msource Map[K V]
hprecomputed content hash of key
keykey of type K
keyeqkey comparison fn (see map-assoc-eq)

A new Map[K V] with the key removed.

(map-dissoc-eq m (cstr-hash k) k tur-cstr-key-eq?)

Since: TCE4

defn

cstr-hash

(cstr-hash [s :cstr] :int)

xxHash64 of a NUL-terminated string's contents.

sthe string to hash

The 64-bit content hash, so equal text hashes equally regardless of pointer identity.

(cstr-hash "key")  ; => <stable content hash>

Since: TCE4

defn

tur-cstr-key-eq?

(tur-cstr-key-eq? [a :cstr b :cstr] :bool)

internal: content equality of two string keys.

defn

tur-int-carrier-eq?

(tur-int-carrier-eq? [a :int b :int] :bool)

float32-map-key-carrier-plan)

The runtime *-eq HAMT carries a key as a single int64_t word and calls the comparator through bool(*)(int64_t,int64_t). For a one-word key (:int, :bool, :cstr) the value rides that word losslessly. A :float32 / :float key does NOT: binding it to the int64_t carrier numerically converts it (1.5 -> 1), and a bool(float,float) comparator invoked through the int64_t ABI reads the wrong registers. Both bugs predate generic dispatch -- they are properties of the one-word carrier itself.

MapKey fixes the carrier per key type, at the only place that knows K concretely -- a typeclass dispatched on a K-typed argument:

mk-box : K -> int64 carrier.  One-word keys: identity.  Float keys:
         a *bit* reinterpret (memcpy of raw bytes via a union), never a
         numeric conversion, so 1.5 survives intact.
mk-cmp : K -> a carrier-ABI comparator bool(int64,int64) (returned as a
         raw function pointer).  Float comparators reinterpret each
         carrier word back to the float and compare by value, so the
         runtime's int64_t ABI is honored.

(hash key) still runs on the unboxed K via Hash[K], so the hash word is unaffected. The boxed form (mk-box) and the unbox in the comparator (mk-cmp) are chosen by the same K, so they always agree. Dispatch is resolved at compile time and the float reinterprets are inlinable, so a one-word map keeps today's representation and cost.

Scope: this covers inline scalar keys (one-word + float32 + float64). Multi-word struct/ADT keys would need a heap-boxed carrier owned by the map (a runtime-ownership change); that remains a separate follow-up. ============================================================================ tur-int-carrier-eq? -- carrier comparator for one-word integral keys.

Used for :int and :bool keys, whose carrier word IS the value, so plain integer equality on the two carrier words is the key equality.

defn

tur-f32-carrier-eq?

(tur-f32-carrier-eq? [a :int b :int] :bool)

carrier comparator for :float32 keys.

Reinterprets each int64 carrier word's low 32 bits back to a float and compares by value (NaN never matches, as with =).

defn

tur-f64-carrier-eq?

(tur-f64-carrier-eq? [a :int b :int] :bool)

carrier comparator for :float (float64) keys.

Reinterprets each int64 carrier word back to a double and compares by value.

defclass

MapKey

(defclass MapKey [a])

key types a typed Map can carry: how to box a key and compare it.

athe key type
(mk-box [x] :int)

x as the HAMT's one-word int64 carrier -- identity for a one-word key, a bit reinterpret (never a numeric conversion) for a float key, a boxed ref for String.

(mk-cmp [x] :int)

a carrier-ABI comparator bool(int64, int64), returned as a raw function pointer; x only picks the instance. It unboxes exactly as mk-box boxed.

(mk-owned? [x] :int)

1 when the boxed carrier is owned by the map and must be released with it (String), else 0.

The map accessors (map-assoc, map-get, ...) dispatch on (Hash K) and (MapKey K) together; see the design note above for why the carrier is fixed per key type.

int, bool, cstr, float32, float, Sym, any, String

Since: WKC (float32-map-key-carrier-plan)

definstance

MapKey[int]

(definstance MapKey [int])

one-word identity carrier; integer-equality comparator.

definstance

MapKey[bool]

(definstance MapKey [bool])

one-word 0/1 carrier; integer-equality comparator.

definstance

MapKey[cstr]

(definstance MapKey [cstr])

pointer carrier; content-equality comparator.

definstance

MapKey[float32]

(definstance MapKey [float32])

bit-reinterpret carrier; float-value comparator.

definstance

MapKey[float]

(definstance MapKey [float])

bit-reinterpret carrier (float64); double-value comparator.

definstance

MapKey[Sym]

(definstance MapKey [Sym])

key a map or set by a dynamically-typed value.

set-of-element-type-is-not-checked / saffron-lang-plan S6: the twin of Hash[any]. Each is? arm narrows x to its payload type and delegates to THAT type's MapKey, so an any holding "two" is boxed and compared exactly as a bare "two" (content-keyed), and an any holding 7.1 as a bare 7.1 (bit-reinterpret, value comparator). The runtime HAMT keeps a comparator PER ENTRY, which is what makes mixing them in one set sound: this is precisely how a heterogeneous set-of already behaved before it had an honest type. An unrecognised payload keys by its type name, like its hash. The Sym arm keeps keyword keys apart (pointer identity, as MapKey[Sym]); without it every keyword collapsed onto the name "Sym" (saffron-dynamic-surface-pass H4). The arms use the .mk-box method spelling on purpose: a bare mk-box resolves in the VALUE namespace, where a user (defn mk-box ...) -- a common name for a Box constructor helper -- legitimately shadows the method (TUR-W0039), and this body is elaborated in the user's program.

saffron-dynamic-surface-pass H4: the Sym instance (MapKey) lives HERE, beside its class, rather than in sym.tur (which the autoloader reads later), so that the any instance below can name it in its Sym arm. Non-orphan either way: the class is defined in this module. MapKey [Sym] -- interned-symbol pointer carrier. Symbols are deduplicated, so pointer identity is content equality; the carrier is the pointer-as-int and the comparator is plain integer (identity) equality.

Since: saffron-lang-plan S6 (MapKey[any])

definstance

MapKey[any]

(definstance MapKey [any])
defn

tur-map-kcheck

(tur-map-kcheck [K V])

============================================================================ The typed Map[K V] accessor surface (TMS3).

One family of accessors -- map-assoc / map-get / map-has? / map-dissoc -- works for ANY scalar key type via (Hash K) + (MapKey K): (hash key) selects the per-K hash, (mk-box key) boxes the key into the int64 carrier (a bit reinterpret for float keys, identity for one-word keys, the pointer for cstr), and (mk-cmp key) supplies a carrier-ABI comparator threaded into the runtime *-eq HAMT. For :int keys mk-box is identity and the comparator is integer =, matching an identity-keyed map; for :cstr keys distinct pointers with equal text collapse to one entry; for :float32 / :float keys the key round-trips by value. Dispatch is resolved at compile time per monomorphization.

The handle is typed Map[K V], so the type checker enforces that the key has type K and the value type V; a key/value-type mismatch is a TUR-E0001.

The accessors are MACROS, not functions. This is deliberate: a value of ANY type V (a :float that must keep its bits, a heap handle like Vec[A] or another Map, a user struct) must ride the map on the single int64 carrier word. A pure-Turmeric generic *function* would ABI-specialize its value parameter to V's concrete C type (e.g. double, Vec__int) and then clash with the int64-carrier runtime bridge. By expanding at the use site, the value flows straight into the inline-C bridge (which carries it as int64, bit-reinterpreting floats and pointers correctly), while a typed key-check (tur-map-kcheck) still enforces that the key has type K against the map's Map[K V] -- so a wrong-typed key is a TUR-E0001. Because they are macros they cannot be passed as first-class function values. ============================================================================ tur-map-kcheck -- internal: enforce key type K against a Map[K V]; returns m.

A typed identity on the map that unifies the key's type with the map's key type K. The accessor macros wrap their map argument in this so a wrong-typed key is rejected (TUR-E0001) even though the runtime dispatch goes through the type-erased int64 carrier.

defn

tur-wide-byval?

(tur-wide-byval? [V])

insert or update a key/value pair; returns a new map.

msource Map[K V]
keykey of type K (must satisfy Hash[K] and MapKey[K]).
Inline float keys (:float32 / :float) are bit-reinterpreted into
the carrier and compared by value; cstr keys are compared by
content; one-word keys are unchanged.
valvalue of type V (any type: scalar, float, or a heap handle)

A new Map[K V] with the entry inserted or updated. Keys equal under Eq[K] (not just identical) collapse to one entry.

(map-assoc (:: (map-new) (Map cstr int)) "name" 1)  ; => Map {"name" -> 1}
(map-assoc (:: (map-new) (Map int int)) 42 100)     ; => Map {42 -> 100}

tur-wide-byval? -- 1 if V is a multi-word (> 8 byte) by-value struct/ADT that the compiler heap-BOXES onto the value carrier, else 0. The compiled backend folds it to a constant per monomorphization (emit_expr.c), in lockstep with the value-boxing decision; this pure-Turmeric fallback body returns 0 for the interpreter (where a value rides as a TuriValue, never a C box) and for any call the backend does not intercept. map-assoc threads it as bit 1 of the owned flag so a Map[K Point]-style boxed value is released on map-free.

Since: multiword-value-box-lifecycle

defn

tur-rc-value?

(tur-rc-value? [V])

1 if V is an rc<T>, else 0. The refcount-side twin of tur-wide-byval?: the compiled backend folds it to a constant per monomorphization (emit_expr.c), and this pure-Turmeric fallback body returns 0 for the interpreter (where an rc value is not carrier-erased) and for any call the backend does not intercept. map-assoc threads it as bit 2 of the owned flag so a Map[K rc<T>] takes a strong reference on insert and releases it when the entry dies. Bits 1 and 2 are mutually exclusive -- a value is either a by-value box or an rc handle, never both.

Since: collections-cannot-hold-rc-values (map side, step c)

defmacro

map-assoc

(map-assoc [m k v])
defmacro

map-get

(map-get [m k])

look up a key; returns the value or 0 (none) if absent.

mMap[K V]
keykey of type K (must satisfy Hash[K] and MapKey[K])

The value of type V, or 0 (none) if absent. For a non-int value type read the result with an ascription, e.g. (:: (map-get m k) :float). When V is any a miss is the nil box (falsy under Saffron truthiness), on both back ends.

(map-get m "name")  ; => value or 0

Since: Phase TMS3 (macro accessor; was map-get-g; GHE3)

defmacro

map-has?

(map-has? [m k])

check whether a key exists in the map.

mMap[K V]
keykey of type K (must satisfy Hash[K] and MapKey[K])

true if the key is present under Eq[K], false otherwise.

(map-has? m "name")  ; => true or false

Since: Phase TMS3 (macro accessor; was map-has-g?; GHE3)

defmacro

map-dissoc

(map-dissoc [m k])

remove a key from the map; returns a new map.

msource Map[K V]
keykey of type K (must satisfy Hash[K] and MapKey[K])

A new Map[K V] with the key removed.

(map-dissoc m "name")  ; => map without "name"

Since: Phase TMS3 (macro accessor; was map-dissoc-g; GHE3)

defn

map-count

(map-count [K V])

return the number of entries in the map.

mMap[K V]

Integer count of key/value pairs.

(map-count my-map)  ; => 3

Since: Phase TM0

defn

map-merge

(map-merge [K V])

merge two maps; values from b win on key collision.

afirst Map[K V]
bsecond Map[K V] (wins on collision)

A new Map[K V] containing all entries from both maps.

(map-merge map-a map-b)  ; => merged map

Since: Phase TM0

defn

map-free

(map-free [K V])

free the Map wrapper (does not free HAMT; use map-free-all for that).

mMap[K V] to free
(map-free my-map)

Since: Phase TM0

defn

map-eq-raw?

(map-eq-raw? [V])

compare two maps for structural equality using a value comparator.

m1first Map[K V]
m2second Map[K V]
val-cmpcomparator fn [a :int b :int] :bool applied to each value pair

true if both maps have identical keys and all corresponding values satisfy val-cmp.

(map-eq? ma mb (fn [a b] (= a b)))  ; => true

Since: Phase TM0

defn

map-eq?

(map-eq? [K V W])

typed structural equality over Map[K V] (delegates to map-eq-raw?).

m1first Map[K V]
m2second Map[K V]
val-cmpcomparator fn [a :int b :int] :bool applied to each value pair

true if both maps have identical keys and all corresponding values satisfy val-cmp.

Since: Phase TM0 (typed surface: TMS3)

defn

map-eq-raw-k?

(map-eq-raw-k? [V])

like map-eq-raw? but compares keys with an explicit carrier comparator (tur_hamt_get_eq) instead of identity. Threading the MapKey[K] comparator here is what makes structural equality content-correct for :cstr keys (distinct pointers with equal text match).

defn

map-eq-k?

(map-eq-k? [K V W])

structural equality over Map[K V] with an explicit key comparator (and value comparator). This is the target of the compiler's content-keyed .eq? dispatch synthesis: at a concrete call site it threads the MapKey[K] comparator resolved for the concrete key type -- for :cstr keys via (mk-cmp (:: 0 K)), for all-:int-field struct keys via (mk-cmp (make-struct K 0 ...)) -- so keys equal by content but stored under distinct pointers/boxes compare equal.

defn

map-eq-dynamic

(map-eq-dynamic [V])

structural equality using the value type's Eq instance. map-eq-dynamic -- internal: structural equality using each map's stamped key comparator (GDE3). val-cmp compares values as int64 carriers.

The key comparator is read from the first map's runtime stamp (set by map-assoc / map-dissoc via tur_hamt_set_eq). For maps built without an explicit comparator (identity keys) the stamp is NULL and pointer identity is used -- which is correct for :int keys.

defn

map-iter-cur-val-as

(map-iter-cur-val-as [V])

typed read of an iter box's current value.

The HAMT iter API hands out ptr<void> for values; this typed accessor bridges that pointer back to V at the carrier level (a single inline-C ascription, no work). Mirrors mutmap-slot-value's shape so the pure-Turmeric map-eq-loop body can compare V-typed values directly.

defn

map-get-dynamic-as

(map-get-dynamic-as [V])

typed lookup against m2's hamt, threading m1's stamped key comparator so content-keyed maps (e.g. cstr keys) stay content-correct. Returns V (cast at the carrier level). The pure-Turmeric loop calls this *after* hamt/has-dynamic? has confirmed the key is present, so the result is always a live value.

defn

map-eq-loop

(map-eq-loop [K V])

pure-Turmeric structural-equality walk over m1's HAMT.

Path A specializes this per (K V) at the Eq [Map] dispatch site; the self-tail-recursive spec lowers to a goto loop (TCO-in-ABI-specs, same gate as vec-eq-loop / mutmap-eq-loop), so deep iteration over large maps runs in constant stack. For each live (h, k, v) of m1 the matching key is looked up in m2's hamt (via hamt/has-dynamic? / map-get-dynamic-as V, threading m1's stamped key comparator so cstr and other content-keyed maps stay content-correct) and the values are compared by val-cmp.

iterhamt/iter-alloc-allocated handle scanning m1
m2-hamtraw HAMT pointer of the second map (from map-hamt)
keyeqstamped key comparator (from hamt/keyeq on m1's hamt)
val-cmpvalue comparator fn [a :V b :V] :bool

true iff every entry of m1 is present in m2 under keyeq with an equal value under val-cmp.

Since: Phase TCO-Eq-MapSet

defn

map-eq-driver

(map-eq-driver [K V])

allocate the iter box, run map-eq-loop, free.

Split out from the Eq [Map] instance so the instance body is a single expression and the (alloc, loop, destroy) sequence is reused.

Since: Phase TCO-Eq-MapSet

definstance

Eq[Map]

(definstance Eq [Map])
defmacro

map-assoc-consuming__

(map-assoc-consuming__ [m k v])

internal: assoc, then free the map assoc'd INTO.

map-literal-leaks-its-intermediate-maps. A HAMT is persistent, so every map-assoc mallocs a fresh {void* hamt} carrier box and returns it. In an N-entry literal that is N+1 boxes of which the author holds only the LAST, so #map{:a 1 :b 2 :c 3} leaked 3 boxes and the generations they retain -- 1080 bytes in 11 allocations -- with map-free called.

This is safe ONLY where the input map is owned by the expression that produced it, which is why it lives here and not in map-assoc: the caller of map-assoc generally holds its argument and must keep it. hamt-of seeds the chain with a fresh (map-empty-for ...) and every later input is a previous step's result, so every generation in the chain is the chain's own.

Freeing the previous generation does not disturb the new one: tur_hamt_free is refcount-safe under structural sharing (it decrements each node and frees only at zero), which is the same guarantee map-free's own body documents.

defmacro

hamt-assoc-each__

(hamt-assoc-each__ [^syntax m & ^syntax kvs])

internal helper for the hamt-of macro.

Threads (map-assoc m k v) over the kv-pairs in xs, returning the final map. Recursive: each step wraps the accumulator in one more map-assoc, so the last pair wins on duplicate keys. Each step also FREES the map it assoc'd into -- see map-assoc-consuming__ for why that is sound here and not in map-assoc itself.

Every parameter of this family (hamt-of, hamt-homog-chain__, this one) is ^syntax: bound as the raw argument form. A plain parameter is substituted into the body before the compile-time evaluation, so m (or a spliced kvs) holding an unannotated lambda value was EVALUATED at compile time and #map{"add" (fn [a b] (+ a b))} failed "compile-time macro evaluation expected a form value".

defn

tur-map-homog__

(tur-map-homog__ [A])

internal: compile-time homogeneity check (see vec.tur).

Both parameters share one type variable, so a call with two differently-typed arguments is a TUR-E0001 at elaboration. No-op body, fixed arity (no allocation).

defmacro

hamt-homog-chain__

(hamt-homog-chain__ [& ^syntax kvs])

internal: enforce homogeneous keys and values.

kvs is k0 v0 k1 v1 ... Chains tur-map-homog__ over adjacent keys and adjacent values so all keys unify to one type and all values to another.

defmacro

hamt-of

(hamt-of [& ^syntax kvs])

variadic Map literal: alternating key/value arguments.

& kvs -- an even number of arguments, alternating key then value.
Keys may be any Hash/MapKey scalar type K (:int, :cstr, :bool,
:float32, :float); all keys must unify to a single K and all
values to a single V, else TUR-E0001 on the offending element.
String keys are compared by content; int/bool by value; float
keys round-trip by value.

A fresh Map[K V] populated with the given entries; later duplicate keys win. map-get returns the value type V, so a non-int value is read with an ascription, e.g. (:: (map-get m k) :cstr).

(hamt-of)              ; => empty Map (annotate at the binding site)
(hamt-of 1 10 2 20)    ; => Map {1 -> 10, 2 -> 20}
(hamt-of "a" 1 "b" 2)  ; => Map {"a" -> 1, "b" -> 2}, content-keyed

Note: this is the lowering target of the #map{...} data literal

(-Xdata-literals).  Writing #map{:a 1 :b 2} is preferred over calling
hamt-of directly.  A non-empty literal is seeded from a value-pinned
empty (map-empty-for) so the key/value types flow from the first pair;
an empty (hamt-of) yields a bare (map-new) that must be annotated by the
surrounding context.

Since: DL1 (data-literals-plan); typed surface: TMS3