A partition of a set is a collection of disjoint subsets where every element of the set is in exactly one of these subsets. The LabelledPartition class defines a labelled collection of sets, with an internal container type for the element subsets (or parts), a label for each part, and an external container that maps the labels to the parts. LabelledPartitions with the same (label and container) type can be merged, with a guarantee that no data is lost and that where labels are equal, commonly labelled parts will be merged into a single part. The standard set theoretic definition of partition requires that the parts be non-empty. For pragmatic convenience, this condition is relaxed in this module because of the labelling: the partition may contain a labelled empty part, which is semantically identical to the the label not being represented in the partition. The LabelledPartition class is a generalization of a Map over lists of elements. Since the LabelledPartition class overloads the names empty, insert, map and fold, it is recommended that the module always be imported qualified, much like Data.Map. \begin{code} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} module Test.GenCheck.Base.LabelledPartition ( LabelledPartition(..) , fromList , relabel , Test.GenCheck.Base.LabelledPartition.filter ) where import Data.Map(Map) import qualified Data.Map as Map import Data.Monoid import qualified Data.Foldable as Fold \end{code} The LabelledPartition class is parameterized over: c - the external container structure v - the internal part structure for the data values It also uses the following: k - the labels for the internal containers a - the actual values in the container In order to support merge, (v a) must form a monoid; v should generally be a free monoid not dependent on the contents of the container. There are exceptions: for example, the container might only retain pass/fail information, in which case the (v a) might just be a generalized boolean value under conjugation. The LabelledPartition methods must obey the following rules: merge empty x == x merge x empty == x merge empty (insert xs k y) == merge xs (insert empty k y) merge (new k xs) (new k ys) == new k (xs `mappend` ys) insert (k x (new k xs)) == new k (x : xs) insert (k x (new k' xs)) == merge (new k [x]) (new k' xs) for k /= k' lookup (insert empty k y) k == Just y lookup (insert empty k y) k' == Nothing for k' /= k lookup empty k' == Nothing \begin{code} class (Fold.Foldable (c k), Functor v) => LabelledPartition c k v where empty :: c k (v a) new :: (Ord k) => k -> [a] -> c k (v a) size :: c k (v a) -> Int insert :: (Ord k) => k -> a -> c k (v a) -> c k (v a) lookup :: (Ord k) => k -> c k (v a) -> Maybe (v a) merge :: (Ord k, Monoid (v a)) => c k (v a) -> c k (v a) -> c k (v a) map :: (Ord k) => (k -> a -> r) -> c k (v a) -> c k (v r) map f = fold (\k x lys -> insert k (f k x) lys) empty fold :: (k -> a -> b -> b) -> b -> c k (v a) -> b toList :: c k (v a) -> [(k, v a)] instance (LabelledPartition c k v, Ord k, Monoid (v r)) => Monoid (c k (v r)) where mempty = empty mappend = merge fromList :: (LabelledPartition c k v, Ord k, Monoid (v a)) => [(k,[a])] -> c k (v a) fromList = Fold.foldr (\(k,ys) -> merge (new k ys)) empty \end{code} The following functions can be used to relabel and filter values in a partition. \begin{description} \item[relabel] reorganizes the container with different grouping labels, \item[filter] extracts only the contents that satisfy a predicate into a new container with the same labels. \end{description} \begin{code} relabel :: (LabelledPartition c k v, LabelledPartition c k' v, Ord k') => (k -> a -> k') -> c k (v a)-> c k' (v a) relabel f cxs = fold (\k x -> insert (f k x) x) empty cxs filter :: (LabelledPartition c k v, Ord k) => (k -> a -> Bool) -> c k (v a) -> c k (v a) filter p cxs = fold cp empty cxs where cp k x ys = if (p k x) then insert k x ys else ys \end{code} One possible LabelledPartition instance is a Haskell Map of lists. It is necessary to override the default insertions and union methods so that no values are overwritten during those operations. Any Map of Monoids would also work, such as Maps of Maps, with the merging of categories carried out with the |mappend|. \begin{code} instance (Ord k) => LabelledPartition Map k [] where empty = Map.empty new k xs = Map.fromList [(k,xs)] size = Map.fold (\xs s -> (length xs) + s) 0 insert k x = Map.insertWith mappend k [x] lookup = Map.lookup merge = Map.unionWith mappend fold f = Map.foldrWithKey (\k x y -> foldr (f k) y x) toList = Map.toList \end{code}