{-# LANGUAGE Trustworthy #-} {-# LANGUAGE EmptyDataDecls, FlexibleContexts, FlexibleInstances , MultiParamTypeClasses, TypeFamilies #-} -- | This module provides the 'HeapItem' type family along with necessary -- instance declarations used to translate between inserted items and the -- priority-value pairs needed by the minimum priority heap of the module -- "Data.Heap.Internal". module Data.Heap.Item ( -- * Type aliases Heap, MinHeap, MaxHeap, MinPrioHeap, MaxPrioHeap -- * The HeapItem type family , HeapItem(..) , MinPolicy, MaxPolicy, FstMinPolicy, FstMaxPolicy -- * Auxiliary functions , splitF ) where import Data.Heap.Internal import Text.Read -- | This type alias is an abbreviation for a 'HeapT' which uses the 'HeapItem' -- instance of @pol item@ to organise its elements. type Heap pol item = HeapT (Prio pol item) (Val pol item) -- | A 'Heap' which will always extract the minimum first. type MinHeap a = Heap MinPolicy a -- | A 'Heap' which will always extract the maximum first. type MaxHeap a = Heap MaxPolicy a -- | A 'Heap' storing priority-value pairs @(prio, val)@. The order of elements -- is solely determined by the priority @prio@, the value @val@ has no influence. -- The priority-value pair with minmal priority will always be extracted first. type MinPrioHeap prio val = Heap FstMinPolicy (prio, val) -- | A 'Heap' storing priority-value pairs @(prio, val)@. The order of elements -- is solely determined by the priority @prio@, the value @val@ has no influence. -- The priority-value pair with maximum priority will always be extracted first. type MaxPrioHeap prio val = Heap FstMaxPolicy (prio, val) -- | @'HeapItem' pol item@ is a type class for items that can be stored in a -- 'HeapT'. A raw @'HeapT' prio val@ only provides a minimum priority heap (i. e. -- @val@ doesn't influence the ordering of elements and the pair with minimal -- @prio@ will be extracted first, see 'HeapT' documentation). The job of this -- class is to translate between arbitrary @item@s and priority-value pairs -- @('Prio' pol item, 'Val' pol item)@, depending on the policy @pol@ to be used. -- This way, we are able to use 'HeapT' not only as 'MinPrioHeap', but also as -- 'MinHeap', 'MaxHeap', 'MaxPrioHeap' or a custom implementation. In short: The -- job of this class is to deconstruct arbitrary @item@s into a @(prio, val)@ -- pairs that can be handled by a minimum priority 'HeapT'. -- -- Example: Consider you want to use @'HeapT' prio val@ as a @'MaxHeap' a@. You -- would have to invert the order of @a@ (e. g. by introducing @newtype InvOrd a -- = InvOrd a@ along with an apropriate 'Ord' instance for it) and then use a -- @type 'MaxHeap' a = 'HeapT' (InvOrd a) ()@. You'd also have to translate -- every @x@ to @(InvOrd x, ())@ before insertion and back after removal in -- order to retrieve your original type @a@. -- -- This functionality is provided by the 'HeapItem' class. In the above example, -- you'd use a 'MaxHeap'. The according instance declaration is of course -- already provided and looks like this (simplified): -- -- @data 'MaxPolicy' -- -- instance ('Ord' a) => 'HeapItem' 'MaxPolicy' a where -- newtype 'Prio' 'MaxPolicy' a = MaxP a deriving ('Eq') -- type 'Val' 'MaxPolicy' a = () -- 'split' x = (MaxP x, ()) -- 'merge' (MaxP x, _) = x -- -- instance ('Ord' a) => 'Ord' ('Prio' 'MaxPolicy' a) where -- 'compare' (MaxP x) (MaxP y) = 'compare' y x -- @ -- -- 'MaxPolicy' is a phantom type describing which 'HeapItem' instance is actually -- meant (e. g. we have to distinguish between 'MinHeap' and 'MaxHeap', which is -- done via 'MinPolicy' and 'MaxPolicy', respectively) and @MaxP@ inverts the -- ordering of @a@, so that the maximum will be on top of the 'HeapT'. -- -- The conversion functions 'split' and 'merge' have to make sure that -- -- (1) @forall p v. 'split' ('merge' (p, v)) == (p, v)@ ('merge' and 'split' -- don't remove, add or alter anything) -- -- (2) @forall p v f. 'fst' ('split' ('merge' (p, f v)) == 'fst' ('split' -- ('merge' (p, v)))@ (modifying the associated value @v@ doesn't alter the -- priority @p@) class (Ord (Prio pol item)) => HeapItem pol item where -- | The part of @item@ that determines the order of elements on a 'HeapT'. data Prio pol item :: * -- | Everything not part of @'Prio' pol item@ type Val pol item :: * -- | Translate an @item@ into a priority-value pair. split :: item -> (Prio pol item, Val pol item) -- | Restore the @item@ from a priority-value pair. merge :: (Prio pol item, Val pol item) -> item {-# RULES "split/merge" forall x. split (merge x) = x #-} -- | Policy type for a 'MinHeap'. data MinPolicy instance (Ord a) => HeapItem MinPolicy a where newtype Prio MinPolicy a = MinP { unMinP :: a } deriving (Eq, Ord) type Val MinPolicy a = () split x = (MinP x, ()) merge (MinP x, _) = x instance (Read a) => Read (Prio MinPolicy a) where readPrec = fmap MinP readPrec readListPrec = fmap (fmap MinP) readListPrec instance (Show a) => Show (Prio MinPolicy a) where show = show . unMinP showsPrec d = showsPrec d . unMinP showList = showList . (fmap unMinP) -- | Policy type for a 'MaxHeap'. data MaxPolicy instance (Ord a) => HeapItem MaxPolicy a where newtype Prio MaxPolicy a = MaxP { unMaxP :: a } deriving (Eq) type Val MaxPolicy a = () split x = (MaxP x, ()) merge (MaxP x, _) = x instance (Ord a) => Ord (Prio MaxPolicy a) where compare (MaxP x) (MaxP y) = compare y x instance (Read a) => Read (Prio MaxPolicy a) where readPrec = fmap MaxP readPrec readListPrec = fmap (fmap MaxP) readListPrec instance (Show a) => Show (Prio MaxPolicy a) where show = show . unMaxP showsPrec d = showsPrec d . unMaxP showList = showList . (fmap unMaxP) -- | Policy type for a @(prio, val)@ 'MinPrioHeap'. data FstMinPolicy instance (Ord prio) => HeapItem FstMinPolicy (prio, val) where newtype Prio FstMinPolicy (prio, val) = FMinP { unFMinP :: prio } deriving (Eq, Ord) type Val FstMinPolicy (prio, val) = val split (p, v) = (FMinP p, v) merge (FMinP p, v) = (p, v) instance (Read prio) => Read (Prio FstMinPolicy (prio, val)) where readPrec = fmap FMinP readPrec readListPrec = fmap (fmap FMinP) readListPrec instance (Show prio) => Show (Prio FstMinPolicy (prio, val)) where show = show . unFMinP showsPrec d = showsPrec d . unFMinP showList = showList . (fmap unFMinP) -- | Policy type for a @(prio, val)@ 'MaxPrioHeap'. data FstMaxPolicy instance (Ord prio) => HeapItem FstMaxPolicy (prio, val) where newtype Prio FstMaxPolicy (prio, val) = FMaxP { unFMaxP :: prio } deriving (Eq) type Val FstMaxPolicy (prio, val) = val split (p, v) = (FMaxP p, v) merge (FMaxP p, v) = (p, v) instance (Ord prio) => Ord (Prio FstMaxPolicy (prio, val)) where compare (FMaxP x) (FMaxP y) = compare y x instance (Read prio) => Read (Prio FstMaxPolicy (prio, val)) where readPrec = fmap FMaxP readPrec readListPrec = fmap (fmap FMaxP) readListPrec instance (Show prio) => Show (Prio FstMaxPolicy (prio, val)) where show = show . unFMaxP showsPrec d = showsPrec d . unFMaxP showList = showList . (fmap unFMaxP) -- | 'split' a function on @item@s to one on priority-value pairs. splitF :: (HeapItem pol item) => (item -> a) -> (Prio pol item, Val pol item) -> a splitF f pv = f (merge pv) {-# INLINE[1] splitF #-} {-# RULES "splitF/split" forall f x. splitF f (split x) = f x #-}