diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2010, Edward Kmett
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Edward Kmett nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/System/Mem/StableName/Dynamic.hs b/System/Mem/StableName/Dynamic.hs
new file mode 100644
--- /dev/null
+++ b/System/Mem/StableName/Dynamic.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE TypeFamilies, Rank2Types #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Mem.StableName.Dynamic
+-- Copyright   :  (c) Edward Kmett 2010
+-- License     :  BSD3
+-- Maintainer  :  ekmett@gmail.com
+-- Stability   :  experimental
+-- Portability :  GHC only
+--
+-- Dynamic stable names are a way of performing fast (O(1)), not-quite-exact comparison between objects.
+--
+-- Dynamic stable names solve the following problem: suppose you want to build a hash table with Haskell objects as keys, but you want to use pointer equality for comparison; maybe because the keys are large and hashing would be slow, or perhaps because the keys are infinite in size. We can't build a hash table using the address of the object as the key, because objects get moved around by the garbage collector, meaning a re-hash would be necessary after every garbage collection.
+-----------------------------------------------------------------------------
+
+module System.Mem.StableName.Dynamic 
+    ( DynamicStableName(..)
+    , hashDynamicStableName
+    , makeDynamicStableName
+    , wrapStableName
+    ) where
+
+import GHC.Prim
+
+import System.Mem.StableName (StableName, makeStableName, hashStableName)
+import Unsafe.Coerce (unsafeCoerce)
+
+{-|
+  An abstract name for an object, that supports equality and hashing.
+
+  Dynamic stable names have the following property:
+
+  * If @sn1 :: DynamicStableName@ and @sn2 :: DynamicStableName@ and @sn1 == sn2@
+   then @sn1@ and @sn2@ were created by calls to @makeStableName@ on 
+   the same object.
+
+  The reverse is not necessarily true: if two dynamic stable names are not
+  equal, then the objects they name may still be equal.  Note in particular
+  that `makeDynamicStableName` may return a different `DynamicStableName` 
+  after an object is evaluated.
+
+  Dynamic Stable Names are similar to Stable Pointers ("Foreign.StablePtr"),
+  but differ in the following ways:
+
+  * There is no @freeDynamicStableName@ operation, unlike "Foreign.StablePtr"s.
+    Dynamic Stable Names are reclaimed by the runtime system when they are no
+    longer needed.
+
+  * There is no @deRefDynamicStableName@ operation.  You can\'t get back from
+    a dynamic stable name to the original Haskell object.  The reason for
+    this is that the existence of a stable name for an object does not
+    guarantee the existence of the object itself; it can still be garbage
+    collected.
+
+-}
+
+newtype DynamicStableName = DynamicStableName (StableName Any)
+
+-- | Makes a 'DynamicStableName' for an arbitrary object.  The object passed as
+-- the first argument is not evaluated by 'makeDynamicStableName'.
+makeDynamicStableName :: t -> IO DynamicStableName
+makeDynamicStableName a = do
+    s <- makeStableName a
+    return (wrapStableName s)
+
+-- | Convert a 'DynamicStableName' to an 'Int'.  The 'Int' returned is not
+-- necessarily unique; several 'DynamicStableName's may map to the same 'Int'
+-- (in practice however, the chances of this are small, so the result
+-- of 'hashDynamicStableName' makes a good hash key).
+hashDynamicStableName :: DynamicStableName -> Int
+hashDynamicStableName (DynamicStableName sn) = hashStableName sn
+
+instance Eq DynamicStableName where
+    DynamicStableName sn1 == DynamicStableName sn2 = sn1 == sn2
+
+wrapStableName :: StableName a -> DynamicStableName
+wrapStableName s = DynamicStableName (unsafeCoerce s)
diff --git a/System/Mem/StableName/Dynamic/Map.hs b/System/Mem/StableName/Dynamic/Map.hs
new file mode 100644
--- /dev/null
+++ b/System/Mem/StableName/Dynamic/Map.hs
@@ -0,0 +1,82 @@
+module System.Mem.StableName.Dynamic.Map
+    ( Map
+    , empty
+    , null
+    , singleton
+    , member
+    , notMember
+    , insert
+    , insertWith
+
+    ) where
+
+import qualified Prelude
+import Prelude hiding (lookup, null)
+import System.Mem.StableName.Dynamic
+import qualified Data.IntMap as IntMap
+import Data.IntMap (IntMap)
+import Unsafe.Coerce (unsafeCoerce)
+
+newtype Map a = Map { getMap :: IntMap [(DynamicStableName, a)] } 
+
+empty :: Map a
+empty = Map IntMap.empty
+
+null :: Map a -> Bool
+null (Map m) = IntMap.null m
+
+singleton :: DynamicStableName -> a -> Map a
+singleton k v = Map $ IntMap.singleton (hashDynamicStableName k) [(k,v)]
+
+member :: DynamicStableName -> Map a -> Bool
+member k m = case lookup k m of
+    Nothing -> False
+    Just _ -> True
+
+notMember :: DynamicStableName -> Map a -> Bool
+notMember k m = not $ member k m 
+
+insert :: DynamicStableName -> a -> Map a -> Map a
+insert k v = Map . IntMap.insertWith (++) (hashDynamicStableName k) [(k,v)] . getMap
+    
+-- | /O(log n)/. Insert with a function for combining the new value and old value.
+-- @'insertWith' f key value mp@
+-- will insert the pair (key, value) into @mp@ if the 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 :: (a -> a -> a) -> DynamicStableName -> a -> Map a -> Map a
+insertWith f k v = Map . IntMap.insertWith go (hashDynamicStableName k) [(k,v)] . getMap 
+    where 
+        go _ ((k',v'):kvs) 
+            | k == k' = (k', f v v') : kvs
+            | otherwise = (k',v') : go undefined kvs
+        go _ [] = []
+
+-- | Same as 'insertWith', but with the combining function applied strictly.
+insertWith' :: (a -> a -> a) -> DynamicStableName -> a -> Map a -> Map a
+insertWith' f k v = Map . IntMap.insertWith go (hashDynamicStableName k) [(k,v)] . getMap 
+    where 
+        go _ ((k',v'):kvs) 
+            | k == k' = let v'' = f v v' in v'' `seq` (k', v'') : kvs
+            | otherwise = (k', v') : go undefined kvs
+        go _ [] = []
+
+-- | /O(log n)/. Lookup the value at a key in the map.
+-- 
+-- The function will return the corresponding value as a @('Just' value)@
+-- or 'Nothing' if the key isn't in the map.
+lookup :: DynamicStableName -> Map v -> Maybe v
+lookup k (Map m) = do
+    pairs <- IntMap.lookup (hashDynamicStableName k) m
+    Prelude.lookup k pairs
+
+find :: DynamicStableName -> Map v -> v
+find k m = case lookup k m of
+    Nothing -> error "Map.find: element not in the map"
+    Just x -> x 
+
+-- | /O(log n)/. The expression @('findWithDefault' def k map)@ returns
+-- the value at key @k@ or returns the default value @def@
+-- when the key is not in the map.
+findWithDefault :: v -> DynamicStableName -> Map v -> v
+findWithDefault dflt k m = maybe dflt id $ lookup k m 
diff --git a/System/Mem/StableName/Map.hs b/System/Mem/StableName/Map.hs
new file mode 100644
--- /dev/null
+++ b/System/Mem/StableName/Map.hs
@@ -0,0 +1,114 @@
+module System.Mem.StableName.Map 
+    ( empty
+    , null
+    , singleton
+    , member
+    , notMember
+    , insert
+    , insertWith
+    , insertWith'
+    , adjust
+    , lookup
+    , find
+    , findWithDefault
+    ) where
+
+import GHC.Prim (Any)
+import qualified Prelude
+import Prelude hiding (lookup, null, any)
+import System.Mem.StableName
+import System.Mem.StableName.Dynamic
+import qualified Data.IntMap as IntMap
+import Data.IntMap (IntMap)
+import Unsafe.Coerce (unsafeCoerce)
+
+newtype Map f = Map { getMap :: IntMap [(DynamicStableName, f Any)] } 
+
+-- unsafe combinators
+any :: f a -> f Any
+any = unsafeCoerce
+
+liftAny1 :: (f a -> f a) -> f Any -> f Any 
+liftAny1 f a = unsafeCoerce $ f (unsafeCoerce a)
+
+empty :: Map f
+empty = Map IntMap.empty
+
+null :: Map f -> Bool
+null (Map m) = IntMap.null m
+
+singleton :: StableName a -> f a -> Map f
+singleton k v = Map $ IntMap.singleton (hashDynamicStableName dk) [(dk, any v)]
+    where
+        dk = wrapStableName k
+
+member :: StableName a -> Map f -> Bool
+member k m = case lookup k m of
+    Nothing -> False
+    Just _ -> True
+
+notMember :: StableName a -> Map f -> Bool
+notMember k m = not $ member k m 
+
+insert :: StableName a -> f a -> Map f -> Map f
+insert k v = 
+    Map . 
+    IntMap.insertWith (++) (hashDynamicStableName dk) [(dk,any v)] . 
+    getMap
+    where 
+        dk = wrapStableName k
+
+-- | /O(log n)/. Insert with a function for combining the new value and old value.
+-- @'insertWith' f key value mp@
+-- will insert the pair (key, value) into @mp@ if the 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 :: (f a -> f a -> f a) -> StableName a -> f a -> Map f -> Map f
+insertWith f k v = Map . IntMap.insertWith go (hashDynamicStableName dk) [(dk,any v)] . getMap 
+    where 
+        dk = wrapStableName k
+        go _ ((k',v'):kvs) 
+            | dk == k' = (k', liftAny1 (f v) v') : kvs
+            | otherwise = (k',v') : go undefined kvs
+        go _ [] = [(dk, any v)]
+
+-- | Same as 'insertWith', but with the combining function applied strictly.
+insertWith' :: (f a -> f a -> f a) -> StableName a -> f a -> Map f -> Map f
+insertWith' f k v = Map . IntMap.insertWith go (hashDynamicStableName dk) [(dk, any v)] . getMap 
+    where 
+        dk = wrapStableName k
+        go _ ((k',v'):kvs) 
+            | dk == k' = let v'' = liftAny1 (f v) v' in v'' `seq` (k', v'') : kvs
+            | otherwise = (k', v') : go undefined kvs
+        go _ [] = [(dk, any v)]
+
+adjust :: (f a -> f a) -> StableName a -> Map f -> Map f
+adjust f k = Map . IntMap.adjust go (hashDynamicStableName dk) . getMap
+    where
+        dk = wrapStableName k
+        go ((k',v):kvs)
+            | dk == k' = (k', liftAny1 f v) : kvs
+            | otherwise = (k', v) : go kvs
+        go [] = []
+
+-- | /O(log n)/. Lookup the value at a key in the map.
+-- 
+-- The function will return the corresponding value as a @('Just' value)@
+-- or 'Nothing' if the key isn't in the map.
+lookup :: StableName a -> Map f -> Maybe (f a)
+lookup k (Map m) = do
+    pairs <- IntMap.lookup (hashDynamicStableName dk) m
+    unsafeCoerce $ Prelude.lookup dk pairs
+    where
+        dk = wrapStableName k 
+
+find :: StableName a -> Map f -> f a 
+find k m = case lookup k m of
+    Nothing -> error "Map.find: element not in the map"
+    Just x -> x 
+
+-- | /O(log n)/. The expression @('findWithDefault' def k map)@ returns
+-- the value at key @k@ or returns the default value @def@
+-- when the key is not in the map.
+findWithDefault :: f a -> StableName a -> Map f -> f a
+findWithDefault dflt k m = maybe dflt id $ lookup k m 
diff --git a/stable-maps.cabal b/stable-maps.cabal
new file mode 100644
--- /dev/null
+++ b/stable-maps.cabal
@@ -0,0 +1,21 @@
+Name:              stable-maps
+Version:           0.0.1
+Synopsis:          Heterogeneous maps keyed by StableNames
+Description:       Provides an API for inserting heterogeneous data in a collection keyed by StableNames and for later retrieving it.
+Homepage:          http://github.com/ekmett/stable-maps
+License:           BSD3
+License-file:      LICENSE
+Author:            Edward Kmett
+Maintainer:        ekmett@gmail.com
+Category:          Math
+Build-type:        Simple
+Cabal-version:     >=1.6
+
+Library
+  Exposed-modules: System.Mem.StableName.Map
+                   System.Mem.StableName.Dynamic
+                   System.Mem.StableName.Dynamic.Map
+  Build-depends:   base >= 4 && < 5,
+                   containers >= 0.3 && < 0.4,
+                   ghc-prim >= 0.2 && < 0.3
+  GHC-Options:     -Wall
