{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DeriveDataTypeable #-} -- | VariableMap is analagous to VariableSet and provides a mutable map ordered -- by key whose changes can be tracked. module Util.VariableMap( VariableMapData, VariableMapUpdate(..), VariableMap, newEmptyVariableMap, newVariableMap, newVariableMapFromFM, updateMap, lookupMap, lookupWithDefaultMap, mapToList, mapToFM, mapToVariableSetSource, addToVariableMap, delFromVariableMap, variableMapToList, lookupVariableMap, getVariableMapByKey, ) where import Data.Maybe import qualified Data.Map as Map import Util.Dynamics import Util.Broadcaster import Util.VariableSet import Util.Sources -- -------------------------------------------------------------------- -- The datatype -- -------------------------------------------------------------------- -- | Describes a map update. For DelUpdate, the second parameter (the one -- of type elt) is irrelevant and may be undefined. newtype VariableMapData key elt = VariableMapData (Map.Map key elt) -- | We recycle the VariableSetUpdate type for this. newtype VariableMapUpdate key elt = VariableMapUpdate (VariableSetUpdate (key,elt)) -- | The Bool indicates whether the operation was successfully carried out. -- We block updating a value which is already in the map, or -- deleting one that isn\'t. update :: Ord key => VariableMapUpdate key elt -> VariableMapData key elt -> (VariableMapData key elt,[VariableMapUpdate key elt],Bool) update (variableUpdate @ (VariableMapUpdate update)) (variableMap @ (VariableMapData map)) = case update of AddElement (key,elt) -> if member key then (variableMap,[],False) else (VariableMapData (Map.insert key elt map),[variableUpdate],True) DelElement (key,_) -> -- we ignore the element, allowing delFromVariable map to put an -- error there. case Map.lookup key map of Just elt -> (VariableMapData (Map.delete key map), [VariableMapUpdate (DelElement (key,elt))],True) Nothing -> (variableMap,[],False) BeginGroup -> (variableMap,[variableUpdate],True) EndGroup -> (variableMap,[variableUpdate],True) where member key = isJust (Map.lookup key map) newtype VariableMap key elt = VariableMap (GeneralBroadcaster (VariableMapData key elt) (VariableMapUpdate key elt)) deriving (Typeable) -- -------------------------------------------------------------------- -- The provider's interface -- -------------------------------------------------------------------- -- | Create a new empty variable map. newEmptyVariableMap :: Ord key => IO (VariableMap key elt) newEmptyVariableMap = do broadcaster <- newGeneralBroadcaster (VariableMapData Map.empty) return (VariableMap broadcaster) -- | Create a new variable map with given contents newVariableMap :: Ord key => [(key,elt)] -> IO (VariableMap key elt) newVariableMap contents = newVariableMapFromFM (Map.fromList contents) newVariableMapFromFM :: Ord key => Map.Map key elt -> IO (VariableMap key elt) newVariableMapFromFM fmap = do broadcaster <- newGeneralBroadcaster (VariableMapData fmap) return (VariableMap broadcaster) -- | Update a variable map in some way. Returns True if the update was -- sucessful (so for insertions, the object is not already there; for -- deletions the object is not there). updateMap :: Ord key => VariableMap key elt -> VariableMapUpdate key elt -> IO Bool updateMap (VariableMap broadcaster) mapUpdate = applyGeneralUpdate broadcaster (update mapUpdate) -- -------------------------------------------------------------------- -- The client's interface -- -------------------------------------------------------------------- -- | Unlike VariableSet, the contents of a variable map are not returned in -- concrete form but as the abstract data type VariableMapData. We provide -- functions for querying this. instance Ord key => HasSource (VariableMap key elt) (VariableMapData key elt) (VariableMapUpdate key elt) where toSource (VariableMap broadcaster) = toSource broadcaster lookupMap :: Ord key => VariableMapData key elt -> key -> Maybe elt lookupMap (VariableMapData map) key = Map.lookup key map lookupWithDefaultMap :: Ord key => VariableMapData key elt -> elt -> key -> elt lookupWithDefaultMap (VariableMapData map) def key = Map.findWithDefault def key map mapToList :: Ord key => VariableMapData key elt -> [(key,elt)] mapToList = Map.toList . mapToFM mapToFM :: Ord key => VariableMapData key elt -> Map.Map key elt mapToFM (VariableMapData map) = map -- -------------------------------------------------------------------- -- An interface to a VariableMap which makes it look like a variable -- set source. -- -------------------------------------------------------------------- data VariableMapSet key elt element = VariableMapSet { variableMap :: VariableMap key elt, mkElement :: key -> elt -> element } -- | Given a variable map and conversion function, produce a VariableSetSource mapToVariableSetSource :: Ord key => (key -> elt -> element) -> VariableMap key elt -> VariableSetSource element mapToVariableSetSource mkElement variableMap = toSource (VariableMapSet {variableMap = variableMap,mkElement = mkElement}) instance Ord key => HasSource (VariableMapSet key elt element) [element] (VariableSetUpdate element) where toSource (VariableMapSet {variableMap = variableMap,mkElement = mkElement}) = (map1 (\ (VariableMapData contents) -> map (uncurry mkElement) (Map.toList contents) ) ) . (map2 (\ (VariableMapUpdate update) -> fmap (\ (key,elt) -> mkElement key elt) update ) ) $ (toSource variableMap) -- -------------------------------------------------------------------- -- A couple of simple access functions -- NB. We don't follow the Registry interface because, without altering -- the design, it would be difficult to implement some Registry functions. -- -------------------------------------------------------------------- addToVariableMap :: Ord key => VariableMap key elt -> key -> elt -> IO Bool addToVariableMap variableMap key elt = updateMap variableMap (VariableMapUpdate (AddElement (key,elt))) delFromVariableMap :: Ord key => VariableMap key elt -> key -> IO Bool delFromVariableMap variableMap key = updateMap variableMap (VariableMapUpdate (DelElement (key, error ("VariableMap.delFromVariableMap")))) variableMapToList :: Ord key => VariableMap key elt -> IO [(key,elt)] variableMapToList (VariableMap broadcaster) = do contents <- readContents broadcaster return (mapToList contents) lookupVariableMap :: Ord key => VariableMap key elt -> key -> IO (Maybe elt) lookupVariableMap (VariableMap broadcaster) key = do (VariableMapData finiteMap) <- readContents broadcaster return (Map.lookup key finiteMap) -- -------------------------------------------------------------------- -- Returns current value of key (if any) in variable map -- NB. This implementation is very inefficient and it is in an inner loop -- in types/LinkManager. However it could be made much better by changing -- the type. -- -------------------------------------------------------------------- getVariableMapByKey :: Ord key => VariableMap key elt -> key -> SimpleSource (Maybe elt) getVariableMapByKey variableMap key = let source1 = toSource variableMap source2 = (map1 (\ (VariableMapData fmap) -> Map.lookup key fmap) ) . (filter2 (\ (VariableMapUpdate update) -> case update of AddElement (key2,elt) | key2 == key -> Just (Just elt) DelElement (key2,elt) | key2 == key -> Just Nothing _ -> Nothing ) ) $ source1 in SimpleSource source2