lattest-lib-0.1.0.0: src/Data/OrdMonad.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{- |
A simple implementation of 'Functor' and 'Monad' with 'Ord' constraints, to make operations on those data types more efficient.
-}
module Data.OrdMonad (
-- * Functors, Monads, and Traversables with Ordering
-- ** Functors with ordering
OrdFunctor,
ordMap,
(<#>),
-- ** Monads with ordering
OrdMonad,
ordBind,
ordReturn,
ordJoin,
-- ** Traversables with ordering
OrdTraversable(..)
)
where
import qualified Data.Set as Set
{-|
Functors with an additional 'Ord' constraint. The primary use case for the 'OrdFunctor' is to treat data structures like 'Set' as a functor, where the 'Ord'-constraint is used for performance reasons.
Implementations of 'ordMap' should adhere to the same laws as for 'fmap' for a @'Functor' F@:
[Identity] @'ordMap' 'id' == 'id'@
[Composition] @'ordMap' (f . g) == 'ordMap' f . 'ordMap' g@
The composition law is only required if the extensionality property of type class 'Eq' holds for the domain of @f@. Effectively, this states
that 'Eq' should behave like proper equality for @f@, or conversely, if @x '==' y@ and @f x '/=' f y@, then compositionality is not expected to hold.
-}
class OrdFunctor f where
-- | Map a function over a functorial type, just like 'fmap', but with an additional 'Ord' constraint.
ordMap :: (Ord b) => (a -> b) -> f a -> f b
-- | An infix synonym for 'ordMap', similar to '<$>'.
(<#>) :: (OrdFunctor f, Ord b) => (a -> b) -> f a -> f b
(<#>) = ordMap
-- | Any 'Functor' is also an 'OrdFunctor', ignoring the 'Ord' constraint..
instance {-# OVERLAPPABLE #-} Functor f => OrdFunctor f where
ordMap = fmap
{-|
Monads with an additional 'Ord' constraint. Analogously to how an 'OrdFunctor' specializes a regular 'Functor', 'OrdMonad' uses the 'Ord'-constraint is used for performance reasons.
Any instance should adhere to the 'Monad' laws, assuming the extensionality property for equality 'Eq'. See 'OrdFunctor' for details.
-}
class (OrdFunctor m) => OrdMonad m where
-- | Return operation, similar to the standard monadic 'return'. No 'Ord' constraint is present here, as comparing values is not needed for injecting a single value into a monadic type.
ordReturn :: a -> m a
-- | Bind operation, using an 'Ord' constraint, similar to the standard monadic bind operation '>>='.
ordBind :: (Ord b) => m a -> (a -> m b) -> m b
-- | Any 'Monad' is also an 'OrdMonad', ignoring the 'Ord' constraint.
instance {-# OVERLAPPABLE #-} Monad m => OrdMonad m where
ordBind = (>>=)
ordReturn = return
-- | Standard monadic 'join', but with an additional 'Ord' constraint.
ordJoin :: (Ord a, OrdMonad m) => m (m a) -> m a
ordJoin mma = ordBind mma id
-- |Traversable with additional 'Ord' constraints.
class (OrdFunctor t, Foldable t) => OrdTraversable t where
{-# MINIMAL ordTraverse, ordSequenceA #-}
-- | Map each element of a structure to an action, evaluate these actions
-- from left to right, and collect the results.
ordTraverse :: (Applicative f, Ord b) => (a -> f b) -> t a -> f (t b)
-- | Evaluate each action in the structure from left to right, and
-- collect the results.
ordSequenceA :: (Applicative f, Ord a) => t (f a) -> f (t a)
-- | Map each element of a structure to a monadic action, evaluate
-- these actions from left to right, and collect the results.
ordMapM :: (Monad m, Ord b) => (a -> m b) -> t a -> m (t b)
ordMapM = ordTraverse
-- | Evaluate each monadic action in the structure from left to
-- right, and collect the results.
ordSequence :: (Monad m, Ord a) => t (m a) -> m (t a)
ordSequence = ordSequenceA
instance {-# OVERLAPPABLE #-} (Foldable t, Traversable t) => OrdTraversable t where
ordTraverse = traverse
ordSequenceA = sequenceA
-- | 'Set' is the the prototypical 'OrdFunctor' instance. It maps a function over the set elements, deduplicating the results.
instance OrdFunctor Set.Set where
ordMap = Set.map
-- | 'Set' is the the prototypical 'OrdMonad' instance, where @s \`'ordBind'\` f@ is the set \( \{ x \in s' \mid s' \in f[s] \} \).
instance OrdMonad Set.Set where
ordBind s f = Set.unions $ Set.map f s
ordReturn = Set.singleton
-- | 'Set' is the prototypical 'OrdTraversable' instance.
instance OrdTraversable Set.Set where
ordSequenceA = foldr (\x xs -> Set.insert <$> x <*> xs) $ pure Set.empty
ordTraverse f = fmap Set.fromList . traverse f . Set.toList