Data.IntMap
Persistent integer-keyed map: a big-endian patricia trie over 64-bit keys.
Keys are I64, the fixed-width signed integer, because a radix trie branches by testing one bit of a machine word. Int is arbitrary-precision and has no highest set bit, so a trie cannot branch on it and no 64-bit branch word can order it; Data.Map serves Int keys through comparison chains. Convert with to_i64 at the boundary, which wraps to the low 64 bits exactly as it does everywhere else.
The branch word of a key is its two’s-complement bit pattern with the sign bit flipped. That transform is a bijection, and it turns signed key order into unsigned branch-word order, so branching is a plain bit test with no sign special case and an in-order walk yields ascending keys, negatives first.
A branch node carries the prefix its keys agree on, the single-bit mask of the position where they split, and the subtrees whose branch bit is zero and one. Every branch keeps two non-empty children, so a node’s mask is exactly the highest bit on which two of its keys differ and a key set determines the tree uniquely. Iteration order therefore depends on the key set alone and never on insertion order, and the stronger statement holds too: maps built from the same bindings in different orders are structurally equal, so intmap_to_list, intmap_fold, and the derived Eq and Show all agree across orders.
Opt-in: not in Base.
Types
IntMap
type IntMap(v)
= IMEmpty
| IMLeaf(I64, v)
| IMBranch(U64, U64, IntMap(v), IntMap(v))
deriving (Eq, Show)
A map from I64 keys to values of type v.
IMEmpty is the empty map, IMLeaf(key, value) a single binding, and IMBranch(prefix, mask, zeros, ones) a split: every key below it agrees with prefix above the single set bit of mask, zeros holds those whose branch bit is clear and ones those whose branch bit is set. Both subtrees of a branch are non-empty.
Functions and Values
intmap_empty
intmap_empty : forall a. Data.IntMap.IntMap(a)
The empty map.
intmap_is_empty(intmap_empty)
true
intmap_singleton
intmap_singleton : forall a. (I64, a) -> Data.IntMap.IntMap(a)
The map binding key to value and nothing else.
intmap_to_list(intmap_singleton(7i64, "a"))
[(7, a)]
intmap_insert
intmap_insert : forall a. (I64, a, Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a)
Insert key with value, overwriting any existing binding.
intmap_lookup(1i64, intmap_insert(1i64, "a", intmap_empty))
Some(a)
intmap_insert_with
intmap_insert_with : forall e0 a. ((a, a) -> a ! {e0}, I64, a, Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a) ! {e0}
Insert key with value, combining a clash as f(new, old).
intmap_to_list(
intmap_insert_with(\(a, b) -> a + b, 1i64, 10, intmap_singleton(1i64, 5)),
)
[(1, 15)]
intmap_lookup
intmap_lookup : forall a. (I64, Data.IntMap.IntMap(a)) -> Option(a)
The value bound to key as Some, or None when absent.
intmap_lookup(2i64, intmap_from_list([(1i64, "a"), (2i64, "b")]))
Some(b)
intmap_member
intmap_member : forall a. (I64, Data.IntMap.IntMap(a)) -> Bool
True when key is bound.
intmap_member(2i64, intmap_from_list([(1i64, "a"), (2i64, "b")]))
true
intmap_delete
intmap_delete : forall a. (I64, Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a)
Remove key (a no-op when absent).
intmap_to_list(intmap_delete(1i64, intmap_from_list([(1i64, "a"), (2i64, "b")])))
[(2, b)]
intmap_size
intmap_size : forall a. (Data.IntMap.IntMap(a)) -> Int
The number of bindings.
intmap_size(intmap_from_list([(1i64, "a"), (2i64, "b")]))
2
intmap_is_empty
intmap_is_empty : forall a. (Data.IntMap.IntMap(a)) -> Bool
True when the map has no bindings.
intmap_is_empty(intmap_empty)
true
intmap_union
intmap_union : forall a. (Data.IntMap.IntMap(a), Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a)
The union of two maps, keeping the left value where a key is in both.
intmap_to_list(
intmap_union(
intmap_from_list([(1i64, "a"), (2i64, "b")]),
intmap_from_list([(2i64, "z"), (3i64, "c")]),
),
)
[(1, a), (2, b), (3, c)]
intmap_union_with
intmap_union_with : forall e0 a. ((a, a) -> a ! {e0}, Data.IntMap.IntMap(a), Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a) ! {e0}
The union of two maps, combining a key present in both as f(left, right).
The two branch nodes are walked together: the shallower one is descended into until the masks meet, so the merge costs the shape of the two tries rather than a re-insertion of every binding.
intmap_to_list(
intmap_union_with(
\(a, b) -> a + b,
intmap_from_list([(1i64, 10), (2i64, 20)]),
intmap_from_list([(2i64, 2), (3i64, 3)]),
),
)
[(1, 10), (2, 22), (3, 3)]
intmap_intersection
intmap_intersection : forall a b. (Data.IntMap.IntMap(a), Data.IntMap.IntMap(b)) -> Data.IntMap.IntMap(a)
The bindings of t1 whose key is also in t2, with t1’s values.
intmap_to_list(
intmap_intersection(
intmap_from_list([(1i64, "a"), (2i64, "b"), (3i64, "c")]),
intmap_from_list([(2i64, "z"), (3i64, "y"), (4i64, "x")]),
),
)
[(2, b), (3, c)]
intmap_difference
intmap_difference : forall a b. (Data.IntMap.IntMap(a), Data.IntMap.IntMap(b)) -> Data.IntMap.IntMap(a)
The bindings of t1 whose key is not in t2.
intmap_to_list(
intmap_difference(
intmap_from_list([(1i64, "a"), (2i64, "b"), (3i64, "c")]),
intmap_from_list([(2i64, "z"), (3i64, "y")]),
),
)
[(1, a)]
intmap_filter
intmap_filter : forall e0 a. ((I64, a) -> Bool ! {e0}, Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(a) ! {e0}
The bindings satisfying keep(key, value).
A surviving branch still splits on the highest bit two of its remaining keys differ on, because a branch whose child empties collapses, so filtering lands on the same tree the survivors would have built from scratch.
intmap_to_list(
intmap_filter(
\(_k, v) -> v > 1,
intmap_from_list([(1i64, 1), (2i64, 2), (3i64, 3)]),
),
)
[(2, 2), (3, 3)]
intmap_map_values
intmap_map_values : forall e0 a b. ((a) -> b ! {e0}, Data.IntMap.IntMap(a)) -> Data.IntMap.IntMap(b) ! {e0}
Apply f to every value, keeping keys and tree shape.
intmap_values(
intmap_map_values(\(v) -> v + 1, intmap_from_list([(1i64, 10), (2i64, 20)])),
)
[11, 21]
intmap_fold
intmap_fold : forall e0 a b. ((a, I64, b) -> a ! {e0}, a, Data.IntMap.IntMap(b)) -> a ! {e0}
Fold f(acc, key, value) over the bindings in ascending key order.
The zero subtree of a branch holds the smaller branch words and branch-word order is key order, so a left-to-right walk is ascending with no sign case.
intmap_fold(\(acc, k, _v) -> acc + k, 0i64, intmap_from_list([(1i64, "a"), (2i64, "b")]))
3
intmap_to_list
intmap_to_list : forall a. (Data.IntMap.IntMap(a)) -> List((I64, a))
The (key, value) pairs in ascending key order, negative keys first.
intmap_to_list(intmap_from_list([(2i64, "b"), (0i64, "z"), (1i64, "a")]))
[(0, z), (1, a), (2, b)]
intmap_keys
intmap_keys : forall a. (Data.IntMap.IntMap(a)) -> List(I64)
The keys in ascending order.
intmap_keys(intmap_from_list([(2i64, "b"), (1i64, "a")]))
[1, 2]
intmap_values
intmap_values : forall a. (Data.IntMap.IntMap(a)) -> List(a)
The values in ascending key order.
intmap_values(intmap_from_list([(1i64, "a"), (2i64, "b")]))
[a, b]
intmap_from_list
intmap_from_list : forall a. (List((I64, a))) -> Data.IntMap.IntMap(a)
Build a map from (key, value) pairs; a later pair overwrites an earlier one with the same key. The result depends only on the surviving bindings, so any ordering of the same list of distinct keys builds the identical tree.
intmap_to_list(intmap_from_list([(2i64, "b"), (1i64, "a")]))
[(1, a), (2, b)]