1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
-- | A monad for binding values to tags to ensure sharing,
-- with the added twist that the value can be polymorphic
-- and each monomorphic instance is bound separately.
module Control.Monad.TagShare(
-- ** Dynamic map
DynMap,
dynEmpty,
dynInsert,
dynLookup,
-- ** Sharing monad
Sharing,
runSharing,
share
) where
import Control.Monad.State
import Data.Typeable
import Data.Dynamic(Dynamic, fromDynamic, toDyn)
import Data.Map as M
-- | A dynamic map with type safe
-- insertion and lookup.
newtype DynMap tag =
DynMap (M.Map (tag, TypeRep) Dynamic)
deriving Show
dynEmpty :: DynMap tag
dynEmpty = DynMap M.empty
dynInsert :: (Typeable a, Ord tag) =>
tag -> a -> DynMap tag -> DynMap tag
dynInsert u a (DynMap m) =
DynMap (M.insert (u,typeOf a) (toDyn a) m)
dynLookup :: (Typeable a, Ord tag) =>
tag -> DynMap tag -> Maybe a
dynLookup u (DynMap m) = hlp fun undefined where
hlp :: Typeable a =>
(TypeRep -> Maybe a) -> a -> Maybe a
hlp f a = f (typeOf a)
fun tr = M.lookup (u,tr) m >>= fromDynamic
-- | A sharing monad
-- with a function that binds a tag to a value.
type Sharing tag a = State (DynMap tag) a
runSharing :: Sharing tag a -> a
runSharing m = evalState m dynEmpty
share :: (Typeable a, Ord tag) =>
tag -> Sharing tag a -> Sharing tag a
share t m = do
mx <- gets $ (dynLookup t)
case mx of
Just e -> return e
Nothing -> mfix $ \e -> do
modify (dynInsert t e)
m
|