hat-2.8.0.0: trans/Relation.hs
-- ------------------------------------------------------------------------------------
-- Relations and their functions
--
-- This interface is based on
-- Diatchki, Jones, Hallgren: "A Formal Specificationof the Haskell 98 Module System"
-- Haskell 2002, ACM
--
-- The type representation is different.
-- It is chosen to be efficient for frequent lookups and also insertions.
module Relation
(Relation
,ascListToRelation,listToRelation,relationToList,emptyRelation,restrictDom,restrictRng
,dom,rng,mapDom,mapRng,intersectRelation,unionRelations,unionRelationsWith,unionLocalRelation
,minusRelation,partitionDom,applyRelation
) where
import qualified Data.Map as Map
import qualified Data.Set as Set
-- The set is always non-empty.
type Relation a b = Map.Map a (Set.Set b)
-- pre-condition: input list is in ascending order in first component
ascListToRelation :: (Ord a, Ord b) => [(a,b)] -> Relation a b
ascListToRelation xs =
Map.fromAscListWith (Set.union) (map (\(x,y) -> (x,Set.singleton y)) xs)
listToRelation :: (Ord a, Ord b) => [(a,b)] -> Relation a b
listToRelation xs =
Map.fromListWith (Set.union) (map (\(x,y) -> (x,Set.singleton y)) xs)
-- post-condition: output list is in ascending order in first component
relationToList :: Relation a b -> [(a,b)]
relationToList r =
concatMap (\(x,ys) -> map (\y -> (x,y)) (Set.toAscList ys)) (Map.toAscList r)
emptyRelation :: Relation a b
emptyRelation = Map.empty
restrictDom :: (Ord a, Ord b) => (a -> Bool) -> Relation a b -> Relation a b
restrictDom p r = Map.filterWithKey (\a _ -> p a) r
restrictRng :: (Ord a, Ord b) => (b -> Bool) -> Relation a b -> Relation a b
restrictRng p = Map.filter (not . Set.null) . Map.map (Set.filter p)
dom :: Ord a => Relation a b -> Set.Set a
dom r = Map.keysSet r
rng :: Ord b => Relation a b -> Set.Set b
rng r = Set.unions (Map.elems r)
mapDom :: (Ord b, Ord c) => (a -> c) -> Relation a b -> Relation c b
mapDom f = Map.mapKeysWith (Set.union) f
mapRng :: (Ord a, Ord b, Ord c) => (b -> c) -> Relation a b -> Relation a c
mapRng f = Map.map (Set.map f)
intersectRelation :: (Ord a, Ord b) => Relation a b -> Relation a b -> Relation a b
intersectRelation = Map.intersectionWith (Set.intersection)
unionRelations :: (Ord a, Ord b) => [Relation a b] -> Relation a b
unionRelations rs = Map.unionsWith (Set.union) rs
-- When one a is related to several b's, the latter are combined.
unionRelationsWith :: (Ord a, Ord b) => (Set.Set b -> Set.Set b -> Set.Set b) -> [Relation a b] -> Relation a b
unionRelationsWith merge rs = Map.unionsWith merge rs
-- Second relation is for a local scope and has precedence over the first relation.
unionLocalRelation :: (Ord a, Ord b) => Relation a b -> Relation a b -> Relation a b
unionLocalRelation r1 r2 = Map.unionWith (const id) r1 r2
minusRelation :: (Ord a, Ord b) => Relation a b -> Relation a b -> Relation a b
minusRelation r1 r2 = Map.differenceWith subtract r1 r2
where
subtract s1 s2 = let s = Set.difference s1 s2
in if Set.null s then Nothing else Just s
partitionDom :: Ord a => (a -> Bool) -> Relation a b -> (Relation a b, Relation a b)
partitionDom p = Map.partitionWithKey (\a bs -> p a)
applyRelation :: (Ord a, Ord b) => Relation a b -> a -> Set.Set b
applyRelation r a = Map.findWithDefault Set.empty a r