std.hashmap
Maps from keys to values by the keys’ hashes: a hash array mapped trie
(HAMT), as Clojure’s, Scala’s and Haskell’s unordered-containers’ maps
are. Import it by a name, import std.hashmap as HashMap
(HashMap.insert), as map’s names are its names too. The map comes
first, as in std.ordmap.
import std.hashmap as HashMap
-- | >>> HashMap.lookup (seen ["x", "y"]) "y"-- | Just (True)seen : List String -> HashMap.HashMap String Boolseen ws = HashMap.fromList (List.map (\w -> MkPair w True) ws)Each level of the trie takes 5 bits of a key’s hash, so a node has up
to 32 children, kept in an array as long as how many there are and
found by a bitmap of which there are (the bits below a child’s, counted,
are where it is); 3572 keys are 3 levels deep, a million 4. A key is
compared once, where its hash has led, with the one there. Where
nothing else holds a node, it is changed in place: a value changed
(update) builds nothing. The map keeps how many keys it binds, as
each walk finds them added or gone, so size reads it; remove says
whether a key it deletes was there.
The keys’ type has a Hash (strings, integers, runes, booleans, and
pairs, lists and Maybes of them) and an Equality, each found as an
instance argument is. The order of toList and foldl is the trie’s,
by the hashes: not the keys’, but the same on every backend.
std.hashtable has the same names, in flat arrays: faster and smaller where one owner fills and reads a map, but a change to a table held elsewhere too copies all of it, and its order is of how it came to be. A map whose old versions are kept is this one’s.
strHash
strHash : String -> IntintHash
intHash : Int -> IntstrHashRange
strHashRange : String -> Int -> Int -> IntThe hash of s’s bytes from i up to j, as strHash (strSlice s i j)
(the same clamping), the slice never made.
>>> strHashRange "hello, world" 7 12 == strHash "world"TruestrEqRange
strEqRange : String -> String -> Int -> Int -> BoolWhether t is s’s bytes from i up to j, as
strEq t (strSlice s i j), the slice never made.
>>> strEqRange "world" "hello, world" 7 12TruetrieSlot
trieSlot : Int -> Int -> Int -> IntHash
theory Hash a where hash : a -> IntA type’s hash: two values equal have the same one.
hash
hash : {a : Type} -> {{hashing : Hash a}} -> a -> IntinstanceHashString
instance instanceHashString : Hash String>>> hash "hello, world" == hash ("hello" ^ ", world")True>>> hash "a" == hash "b"FalseinstanceHashInt
instance instanceHashInt : Hash Int>>> List.map (\(i : Int) -> hash i == hash (i + 1)) [0, 1180591620717411303424]Cons False (Cons False Nil)instanceHashRune
instance instanceHashRune : Hash RuneinstanceHashBool
instance instanceHashBool : Hash Boolcombine
combine : Int -> Int -> Intthe hash of what is hashed h so far, then of x’s hash: in order,
so that MkPair a b and MkPair b a differ
instanceHashPair
instance instanceHashPair : {a b : Type} -> {{hashA : Hash a}} -> {{hashB : Hash b}} -> Hash (a, b)>>> hash (MkPair 1 2 : Pair Int Int) == hash (MkPair 2 1 : Pair Int Int)FalseinstanceHashMaybe
instance instanceHashMaybe : {a : Type} -> {{hashing : Hash a}} -> Hash (Maybe a)instanceHashList
instance instanceHashList : {a : Type} -> {{hashing : Hash a}} -> Hash (List a)>>> hash (["a", "b"] : List String) == hash (["b", "a"] : List String)FalseKeyView
theory KeyView k q where hashOf : q -> Int matches : k -> q -> Bool keyOf : q -> kA view of a key, q, that stands for a key of type k without
being one: what a map’s updateBy and lookupBy look a key up by,
so that the key is made (keyOf) only where it is new to the map.
Its hash is the key’s, and it matches the key it stands for and no
other: hashOf q == hash (keyOf q), and matches k q where
equal k (keyOf q).
Slice
data Slice = MkSlice String Int IntA part of a string: its bytes from one place up to another, as
strSlice takes them, the slice not made.
slice
slice : String -> Int -> Int -> SliceThe part of s from i up to j, as a Slice (where its type is
not known, as a view’s is not, a tag is not enough).
instanceKeyViewStringSlice
instance instanceKeyViewStringSlice : KeyView String SliceA string’s part stands for the string strSlice would make of it,
hashed and compared where it is.
>>> instanceKeyViewStringSlice.hashOf (MkSlice "hello, world" 7 12) == hash "world"True>>> instanceKeyViewStringSlice.matches "world" (MkSlice "hello, world" 7 12)TrueTrie
data Trie k v = Vacant | Leaf Int k v | Collision Int (List (k, v)) | Branch Int (Array (Trie k v))A trie: nothing (only the whole map’s, never a child); a key, its hash and its value; keys of one hash, each with its value; or a node of children, a bitmap of which of the 32 there are.
HashMap
data HashMap k v = MkHashMap Int (Trie k v)A map: how many keys it binds, and its trie.
empty
empty : {k v : Type} -> HashMap k vThe map of no keys.
Walked
data Walked k v a = MkWalked (Trie k v) a (Array (Trie k v))update
update : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> v -> (v -> v -> v) -> HashMap k vThe map with k bound to v, or, where it is bound, to f v old
(Haskell’s insertWith): a value changed in one walk down the trie.
insert
insert : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> v -> HashMap k vThe map with k bound to v (instead of what it was, if it was).
lookup
lookup : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> Maybe vWhat k is bound to, if it is.
remove
remove : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> (Maybe v, HashMap k v)What k was bound to, if it was, and the map without it (Rust’s
remove): a delete that says whether the key was there, in one walk
down the trie.
delete
delete : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> HashMap k vThe map without k (as it was, if k was not in it; remove says
which).
alter
alter : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> (Maybe v -> Maybe v) -> HashMap k vThe map with k bound as f says, given what it is bound to, if it
is: to f’s value, or (Nothing) to none (Haskell’s alter), in
one walk down the trie.
foldl
foldl : {k v b : Type} -> (b -> k -> v -> b) -> b -> HashMap k v -> bEach key and its value, from z, in the trie’s order (the hashes’).
size
size : {k v : Type} -> HashMap k v -> IntHow many keys are bound: kept as they are bound and deleted, not counted.
toList
toList : {k v : Type} -> HashMap k v -> List (k, v)The keys and their values, in the trie’s order (the hashes’, not the keys’): sort them for another.
member
member : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> HashMap k v -> k -> BoolWhether k is bound in it.
>>> List.map (member (insert (empty : HashMap String Int) "a" 1)) ["a", "b"]Cons True (Cons False Nil)keys
keys : {k v : Type} -> HashMap k v -> List kThe keys, in toList’s order.
>>> keys (insert (empty : HashMap String Int) "a" 1)Cons "a" Nilvalues
values : {k v : Type} -> HashMap k v -> List vThe values, in toList’s order.
>>> values (insert (empty : HashMap String Int) "a" 1)Cons 1 NilinstanceIterateHashMap
instance instanceIterateHashMap : {k v : Type} -> Iterate (HashMap k v)A hash map, each key and its value in the trie’s order, as toList
has them: for p in m goes through that list.
fromList
fromList : {k v : Type} -> {{hashing : Hash k}} -> {{equality : Equality k}} -> List (k, v) -> HashMap k vThe map of the pairs, a later one’s value for a key twice.
>>> lookup (fromList [MkPair "a" 1, MkPair "b" 2]) "b"Just 2>>> lookup (fromList [MkPair "a" 1, MkPair "b" 2]) "c"Nothing>>> lookup (insert (fromList [MkPair 2 "deux"]) 2 "two") 2Just "two">>> size (fromList [MkPair "a" 1, MkPair "b" 2, MkPair "a" 3])2>>> foldl (\n k v -> n + k * v) 0 (fromList [MkPair 2 3, MkPair 4 5])26>>> List.sortOn (\p -> fst p) (toList (fromList [MkPair "b" 2, MkPair "a" 1]))Cons (MkPair "a" 1) (Cons (MkPair "b" 2) Nil)>>> toList (update (fromList [MkPair "a" 2]) "a" 1 (\new old -> new + old))Cons (MkPair "a" 3) Nil>>> lookup (update (fromList [MkPair "a" 2]) "b" 5 (\new old -> new + old)) "b"Just 5>>> toList (delete (fromList [MkPair "a" 1, MkPair "b" 2]) "a")Cons (MkPair "b" 2) Nil>>> size (delete (fromList [MkPair 1 "one"]) 7)1>>> size (delete (fromList [MkPair 1 "one", MkPair 2 "two"]) 1)1>>> fst (remove (fromList [MkPair "a" 1, MkPair "b" 2]) "a")Just 1>>> fst (remove (insert (empty : HashMap String Int) "a" 1) "c")Nothing>>> toList (snd (remove (fromList [MkPair "a" 1, MkPair "b" 2]) "a"))Cons (MkPair "b" 2) Nil>>> size (alter (fromList [MkPair 1 "one"]) 2 (\old -> Just "two"))2>>> toList (alter (fromList [MkPair "a" 2]) "a" (\old -> Just (maybe 1 (\n -> n + 1) old)))Cons (MkPair "a" 3) Nil>>> toList (alter (fromList [MkPair 1 "one"]) 1 (\old -> Nothing))Nil>>> lookup (alter (fromList [MkPair 1 "one"]) 3 (\old -> Just "three")) 3Just "three"updateBy
updateBy : {k v q : Type} -> {{view : KeyView k q}} -> HashMap k v -> q -> v -> (v -> v -> v) -> HashMap k vupdate of the key a view stands for (KeyView): the key made of
it only where it is new to the map, so that counting the parts of a
string (slice), most of them there already, makes no string for
those.
>>> lookup (updateBy (fromList [MkPair "ab" 1]) (slice "xaby" 1 3) 5 (\new old -> new + old)) "ab"Just 6>>> toList (updateBy (empty : HashMap String Int) (slice "xaby" 1 3) 5 (\new old -> new + old))Cons (MkPair "ab" 5) NillookupBy
lookupBy : {k v q : Type} -> {{view : KeyView k q}} -> HashMap k v -> q -> Maybe vlookup of the key a view stands for (KeyView), no key made.
>>> lookupBy (fromList [MkPair "ab" 1]) (slice "xaby" 1 3)Just 1>>> lookupBy (fromList [MkPair "ab" 1]) (slice "xaby" 1 2)Nothing