{-# 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)