Hash Array Mapped Tries ======================= One of the prominent features of the [Clojure][1] language are a set of [immutable data structures][2] with efficient manipulation operations. One of the most innovative and important is the persistent hash map based on the *hash array mapped trie* (HAMT). This project is a port of this structure to Haskell, as Data.HamtMap. The interface has been kept as consistent as possible with Data.Map. [1]: http://clojure.org/ [2]: http://clojure.org/datatypes Basic usage ----------- Here's a demo of what you can do with a HamtMap: ghci> :m + Data.HamtMap ghci> empty Data.HashTable.hashString -- an empty HamtMap (requires a key hash function) fromList hashFn [] ghci> insert "foo" 1 it fromList hashFn [("foo",1)] ghci> insert "bar" 42 it fromList hashFn [("foo",1),("bar",42)] ghci> insert "qux" 123 it fromList hashFn [("qux",12),("foo",1),("bar",42)] ghci> insert "qux" 13 it -- inserting an existing key overwrites by default fromList hashFn [("qux",13),("foo",1),("bar",42)] ghci> let a = it ghci> a ! "foo" 1 ghci> a ! "baz" -- using (!) is unsafe *** Exception: array index out of range: element not in the map ghci> Data.HamtMap.lookup "bar" a Just 42 ghci> Data.HamtMap.lookup "baz" a -- 'lookup' returns a safe Maybe Nothing ghci> adjust succ "foo" a -- apply a function to a value fromList hashFn [("qux",13),("foo",2),("bar",42)] ghci> Data.HamtMap.map succ a -- apply a function to all values fromList hashFn [("qux",14),("foo",2),("bar",43)] ghci> keys a ["qux","foo","bar"] ghci> elems a [13,1,42] ghci> fromList Data.HashTable.hashString [("a", 1), ("b", 2), ("c", 3)] fromList hashFn [("b",2),("c",3),("a",1)] ghci> toList it [("b",2),("c",3),("a",1)] Installation ------------ To try it yourself, just do the usual: $ runghc Setup.hs configure --user $ runghc Setup.hs build $ runghc Setup.hs install Performance ----------- The single-element operations for the hash map have logarithmic asymtotic runtime complexity. However, it is implemented as a 32-ary tree, which means it never exceeds a depth of 7 nodes, so you can treat them as constant-time operations (for relatively large constants). How it works ------------ I wrote this code after reading the following explanatory blog posts on how the Clojure version works. They should also provide a decent birds-eye overview of my Haskell implementation. * [Understanding Clojure’s PersistentHashMap ](http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice/) * [Assoc and Clojure’s PersistentHashMap: part II ](http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii/) To do ----- * Match Data.Map in completeness * Performance tuning * Efficient implementations of (//), etc. based on fromList