Skip to content
Browse files

Renamed (hopefully for the last time) to HamtMap

  • Loading branch information...
1 parent 8655731 commit be9a6ba7031a9b39ad3a35e81142c35daf230b20 @exclipy committed Jan 18, 2011
Showing with 65 additions and 67 deletions.
  1. +40 −40 Data/{PHashMap.hs → HamtMap.hs}
  2. +17 −19 README.md
  3. +5 −5 benchmarks/benchmark.hs
  4. +2 −2 phashmap.cabal → hamtmap.cabal
  5. +1 −1 tests/tests.hs
View
80 Data/PHashMap.hs → Data/HamtMap.hs
@@ -1,28 +1,28 @@
-module Data.PHashMap (
- -- * PHashMap type
- PHashMap
+module Data.HamtMap (
+ -- * HamtMap type
+ HamtMap
-- * Operators
- , (Data.PHashMap.!)
+ , (Data.HamtMap.!)
-- * Query
, member
, notMember
- , Data.PHashMap.lookup
+ , Data.HamtMap.lookup
-- * Construction
, empty
, singleton
-- * Insertion
, insert
, insertWith
-- * Delete\/Update
- , Data.PHashMap.delete
+ , Data.HamtMap.delete
, adjust
, update
, alter
-- * Traversal
- , Data.PHashMap.map
+ , Data.HamtMap.map
, mapWithKey
-- * Conversion
- , Data.PHashMap.elems
+ , Data.HamtMap.elems
, keys
, toList
, fromListWith
@@ -38,17 +38,17 @@ import Data.List hiding (insert, lookup)
import Data.Array as A
import Prelude as P
--- | A PHashMap from keys @k@ to values @v@
-data (Eq k) => PHashMap k v = HM {
+-- | A HamtMap from keys @k@ to values @v@
+data (Eq k) => HamtMap k v = HM {
hashFn :: k -> Int32
, root :: Node k v
}
-instance (Eq k, Show k, Show v) => Show (PHashMap k v) where
+instance (Eq k, Show k, Show v) => Show (HamtMap k v) where
show (HM h r) = show r
- -- show = ("fromList hashFn "++).show.(Data.PHashMap.toList)
+ -- show = ("fromList hashFn "++).show.(Data.HamtMap.toList)
-instance (Eq k, NFData k, NFData v) => NFData (PHashMap k v) where
+instance (Eq k, NFData k, NFData v) => NFData (HamtMap k v) where
rnf (HM f r) = f `seq` rnf r
instance (Eq k, NFData k, NFData v) => NFData (Node k v) where
@@ -101,14 +101,14 @@ isEmptyNode _ = False
hashFragment shift hash = (hash `shiftR` shift) .&. fromIntegral mask
--- | @('empty' hashFn)@ is the empty PHashMap, with hashFn being the key hash function.
-empty :: (Eq k) => (k -> Int32) -> PHashMap k v
+-- | @('empty' hashFn)@ is the empty HamtMap, with hashFn being the key hash function.
+empty :: (Eq k) => (k -> Int32) -> HamtMap k v
empty hashFn = HM hashFn EmptyNode
--- | @('singleton' hashFn key value)@ is a single-element PHashMap holding @(key, value)@
-singleton :: (Eq k) => (k -> Int32) -> k -> v -> PHashMap k v
+-- | @('singleton' hashFn key value)@ is a single-element HamtMap holding @(key, value)@
+singleton :: (Eq k) => (k -> Int32) -> k -> v -> HamtMap k v
singleton hashFn key value = HM hashFn $ LeafNode (hashFn key) key value
@@ -120,7 +120,7 @@ data Change = Removed | Modified | Nil | Added deriving Eq
-- | The expression (@'alter' f k map@) alters the value @x@ at @k@, or absence thereof.
-- 'alter' can be used to insert, delete, or update a value in a 'Map'.
-- In short : @'lookup' k ('alter' f k m) = f ('lookup' k m)@.
-alter :: (Eq k) => (Maybe v -> Maybe v) -> k -> PHashMap k v -> PHashMap k v
+alter :: (Eq k) => (Maybe v -> Maybe v) -> k -> HamtMap k v -> HamtMap k v
alter updateFn key (HM hashFn root) =
HM hashFn $ alterNode 0 updateFn (hashFn key) key root
@@ -276,48 +276,48 @@ alterNode shift updateFn hash key node@(ArrayNode numChildren subNodes) =
-- will insert the pair (key, value) into @mp@ if key does
-- not exist in the map. If the key does exist, the function will
-- insert the pair @(key, f new_value old_value)@.
-insertWith :: (Eq k) => (v -> v -> v) -> k -> v -> PHashMap k v -> PHashMap k v
+insertWith :: (Eq k) => (v -> v -> v) -> k -> v -> HamtMap k v -> HamtMap k v
-insertWith accumFn key value hashMap =
+insertWith accumFn key value hm =
let fn :: (v -> v -> v) -> v -> Maybe v -> Maybe v
fn accumFn x' Nothing = Just x'
fn accumFn x' (Just x) = Just $ accumFn x' x
- in alter (fn accumFn value) key hashMap
+ in alter (fn accumFn value) key hm
-- | Insert a new key and value in the map.
-- If the key is already present in the map, the associated value is
-- replaced with the supplied value. 'insert' is equivalent to
-- @'insertWith' 'const'@.
-insert :: (Eq k) => k -> v -> PHashMap k v -> PHashMap k v
+insert :: (Eq k) => k -> v -> HamtMap k v -> HamtMap k v
insert = insertWith const
-- | The expression (@'update' f k map@) updates the value @x@
-- at @k@ (if it is in the map). If (@f x@) is 'Nothing', the element is
-- deleted. If it is (@'Just' y@), the key @k@ is bound to the new value @y@.
-update :: (Eq k) => (v -> Maybe v) -> k -> PHashMap k v -> PHashMap k v
+update :: (Eq k) => (v -> Maybe v) -> k -> HamtMap k v -> HamtMap k v
update updateFn = alter ((=<<) updateFn)
-- | Delete a key and its value from the map. When the key is not
-- a member of the map, the original map is returned.
-delete :: (Eq k) => k -> PHashMap k v -> PHashMap k v
+delete :: (Eq k) => k -> HamtMap k v -> HamtMap k v
delete = alter (const Nothing)
-- | Update a value at a specific key with the result of the provided function.
-- When the key is not a member of the map, the original map is returned.
-adjust :: (Eq k) => (v -> v) -> k -> PHashMap k v -> PHashMap k v
+adjust :: (Eq k) => (v -> v) -> k -> HamtMap k v -> HamtMap k v
adjust updateFn = alter ((=<<) ((Just).updateFn))
-- | Map a function over all values in the map.
-mapWithKey :: (Eq k) => (k -> v -> v) -> PHashMap k v -> PHashMap k v
+mapWithKey :: (Eq k) => (k -> v -> v) -> HamtMap k v -> HamtMap k v
mapWithKey mapFn (HM hashFn root) =
HM hashFn $ mapWithKeyNode mapFn root
@@ -345,7 +345,7 @@ arrayMap fn arr = array (bounds arr) $ P.map (\(key, value) -> (key, fn value))
-- | Map a function over all values in the map.
-map :: (Eq k) => (v -> v) -> PHashMap k v -> PHashMap k v
+map :: (Eq k) => (v -> v) -> HamtMap k v -> HamtMap k v
map fn = mapWithKey (const fn)
@@ -354,7 +354,7 @@ map fn = mapWithKey (const fn)
--
-- The function will return the corresponding value as @('Just' value)@,
-- or 'Nothing' if the key isn't in the map.
-lookup :: (Eq k) => k -> PHashMap k v -> Maybe v
+lookup :: (Eq k) => k -> HamtMap k v -> Maybe v
lookup key (HM hashFn root) = lookupNode 0 (hashFn key) key root
@@ -385,26 +385,26 @@ lookupNode shift hash key (ArrayNode _numChildren subNodes) =
-- | Find the value at a key.
-- Calls 'error' when the element can not be found.
-(!) :: (Eq k) => PHashMap k v -> k -> v
+(!) :: (Eq k) => HamtMap k v -> k -> v
-hashMap ! key = maybe (error "element not in the map")
- id
- (Data.PHashMap.lookup key hashMap)
+hm ! key = maybe (error "element not in the map")
+ id
+ (Data.HamtMap.lookup key hm)
-- | Is the key a member of the map? See also 'notMember'.
-member :: (Eq k) => k -> PHashMap k v -> Bool
+member :: (Eq k) => k -> HamtMap k v -> Bool
-member key hashMap = maybe False (const True) (Data.PHashMap.lookup key hashMap)
+member key hm = maybe False (const True) (Data.HamtMap.lookup key hm)
-- | Is the key a member of the map? See also 'member'.
-notMember :: (Eq k) => k -> PHashMap k v -> Bool
+notMember :: (Eq k) => k -> HamtMap k v -> Bool
notMember key = not.(member key)
-- | Convert to a list of key\/value pairs.
-toList :: (Eq k) => PHashMap k v -> [(k, v)]
+toList :: (Eq k) => HamtMap k v -> [(k, v)]
toList (HM _hashFn root) = toListNode root
@@ -425,7 +425,7 @@ toListNode (ArrayNode _numChildren subNodes) =
-- | Build a map from a list of key\/value pairs with a combining function.
-fromListWith :: (Eq k) => (k -> Int32) -> (v -> v -> v) -> [(k, v)] -> PHashMap k v
+fromListWith :: (Eq k) => (k -> Int32) -> (v -> v -> v) -> [(k, v)] -> HamtMap k v
fromListWith hashFn combineFn assocs =
HM hashFn $ fromListNode 0 combineFn $ P.map (\(k, v) -> ((hashFn k), k, v)) assocs
@@ -483,20 +483,20 @@ fromListNode shift combineFn hkvs =
-- | Build a map from a list of key\/value pairs.
-- If the list contains more than one value for the same key, the last value
-- for the key is retained.
-fromList :: (Eq k) => (k -> Int32) -> [(k, v)] -> PHashMap k v
+fromList :: (Eq k) => (k -> Int32) -> [(k, v)] -> HamtMap k v
fromList hashFn assocs =
fromListWith hashFn const assocs
-- | Return all keys of the map.
-keys :: (Eq k) => PHashMap k v -> [k]
+keys :: (Eq k) => HamtMap k v -> [k]
keys = (P.map fst).toList
-- | Return all elements of the map.
-elems :: (Eq k) => PHashMap k v -> [v]
+elems :: (Eq k) => HamtMap k v -> [v]
elems = (P.map snd).toList
View
36 README.md
@@ -1,12 +1,12 @@
-Persistent Vectors and HashMaps for Haskell
-===========================================
+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*.
+*hash array mapped trie* (HAMT).
-This project is a port of this structure to Haskell, as Data.PHashMap. The
+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/
@@ -15,11 +15,11 @@ interface has been kept as consistent as possible with Data.Map.
Basic usage
-----------
-Here's a demo of what you can do with a PHashMap:
+Here's a demo of what you can do with a HamtMap:
- ghci> :m + Data.PHashMap
+ ghci> :m + Data.HamtMap
ghci> empty Data.HashTable.hashString
- -- an empty PHashMap (requires a key hash function)
+ -- an empty HamtMap (requires a key hash function)
fromList hashFn []
ghci> insert "foo" 1 it
@@ -41,16 +41,16 @@ Here's a demo of what you can do with a PHashMap:
ghci> a ! "baz" -- using (!) is unsafe
*** Exception: array index out of range: element not in the map
- ghci> Data.PHashMap.lookup "bar" a
+ ghci> Data.HamtMap.lookup "bar" a
Just 42
- ghci> Data.PHashMap.lookup "baz" a -- 'lookup' returns a safe Maybe
+ 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.PHashMap.map succ a -- apply a function to all values
+ ghci> Data.HamtMap.map succ a -- apply a function to all values
fromList hashFn [("qux",14),("foo",2),("bar",43)]
ghci> keys a
@@ -78,10 +78,10 @@ To try it yourself, just do the usual:
Performance
-----------
-The single-element operations for the hash map technically runs in logarithmic
-time. 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).
+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
------------
@@ -96,10 +96,8 @@ my Haskell implementation.
](http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii/)
-To do (help appreciated!)
--------------------------
+To do
+-----
* Match Data.Map in completeness
* Performance tuning
- * Efficient implementations of (//), fromListWith, etc. based on fromList
-* Unit tests
-* Benchmarks
+ * Efficient implementations of (//), etc. based on fromList
View
10 benchmarks/benchmark.hs
@@ -11,7 +11,7 @@ import Data.Hashable (Hashable(hash))
import Data.Int (Int32)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
-import qualified Data.PHashMap as HM
+import qualified Data.HamtMap as HM
import Data.List (foldl')
import Data.Maybe (fromMaybe)
import Prelude hiding (lookup)
@@ -24,7 +24,7 @@ hashBS = fromIntegral . hash
main :: IO ()
main = do
- let hmbs = HM.fromList hashBS elemsBS :: HM.PHashMap BS.ByteString Int
+ let hmbs = HM.fromList hashBS elemsBS :: HM.HamtMap BS.ByteString Int
defaultMainWith defaultConfig
(liftIO . evaluate $ rnf [hmbs])
[ bench "fromList" $ nf (HM.fromList hashBS) elemsBS
@@ -39,13 +39,13 @@ main = do
elemsBS = zip keysBS [1..n]
keysBS = rnd 8 n
-lookup :: Eq k => [k] -> HM.PHashMap k Int -> Int
+lookup :: Eq k => [k] -> HM.HamtMap k Int -> Int
lookup xs m = foldl' (\z k -> fromMaybe z (HM.lookup k m)) 0 xs
-insert :: Eq k => [(k, Int)] -> HM.PHashMap k Int -> HM.PHashMap k Int
+insert :: Eq k => [(k, Int)] -> HM.HamtMap k Int -> HM.HamtMap k Int
insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
-delete :: Eq k => [k] -> HM.PHashMap k Int -> HM.PHashMap k Int
+delete :: Eq k => [k] -> HM.HamtMap k Int -> HM.HamtMap k Int
delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
-- | Generate a number of fixed length strings where the content of
View
4 phashmap.cabal → hamtmap.cabal
@@ -1,4 +1,4 @@
-name: phashmap
+name: hamtmap
version: 0.2
cabal-version: >= 1.2
synopsis: A purely functional and persistent hash map
@@ -21,6 +21,6 @@ stability: experimental
library
build-depends: base >= 4 && < 5, array, deepseq
- exposed-modules: Data.PHashMap
+ exposed-modules: Data.HamtMap
other-modules: Data.BitUtil
ghc-options: -O2
View
2 tests/tests.hs
@@ -1,7 +1,7 @@
import Test.QuickCheck
import Test.QuickCheck.Batch
import Data.Hashable
-import Data.PHashMap as HM
+import Data.HamtMap as HM
import Data.Int
import Data.List (foldl', sort)
import Data.Maybe (isNothing)

0 comments on commit be9a6ba

Please sign in to comment.
Something went wrong with that request. Please try again.