packages feed

hamtmap-0.2: README.md

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