stable-maps (empty) → 0.0.1
raw patch · 6 files changed
+327/−0 lines, 6 filesdep +basedep +containersdep +ghc-primsetup-changed
Dependencies added: base, containers, ghc-prim
Files
- LICENSE +31/−0
- Setup.hs +2/−0
- System/Mem/StableName/Dynamic.hs +77/−0
- System/Mem/StableName/Dynamic/Map.hs +82/−0
- System/Mem/StableName/Map.hs +114/−0
- stable-maps.cabal +21/−0
+ LICENSE view
@@ -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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ System/Mem/StableName/Dynamic.hs view
@@ -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)
+ System/Mem/StableName/Dynamic/Map.hs view
@@ -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
+ System/Mem/StableName/Map.hs view
@@ -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
+ stable-maps.cabal view
@@ -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