-- |
-- A monoid that combines according to priorities, allowing some values to take precedence over others.
module Web.Route.Invertible.Monoid.Prioritized
  ( Prioritized(..)
  ) where

import Data.Semigroup (Semigroup((<>)))

-- |A trival monoid allowing each item to be given a priority when combining.
data Prioritized a = Prioritized
  { Prioritized a -> Int
priority :: !Int -- ^ The priority this value, where larger numeric values have higher priority and take precedence over lower priorities.
  , Prioritized a -> a
prioritized :: !a -- ^ The prioritized value.
  } deriving (Prioritized a -> Prioritized a -> Bool
(Prioritized a -> Prioritized a -> Bool)
-> (Prioritized a -> Prioritized a -> Bool) -> Eq (Prioritized a)
forall a. Eq a => Prioritized a -> Prioritized a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Prioritized a -> Prioritized a -> Bool
$c/= :: forall a. Eq a => Prioritized a -> Prioritized a -> Bool
== :: Prioritized a -> Prioritized a -> Bool
$c== :: forall a. Eq a => Prioritized a -> Prioritized a -> Bool
Eq, Int -> Prioritized a -> ShowS
[Prioritized a] -> ShowS
Prioritized a -> String
(Int -> Prioritized a -> ShowS)
-> (Prioritized a -> String)
-> ([Prioritized a] -> ShowS)
-> Show (Prioritized a)
forall a. Show a => Int -> Prioritized a -> ShowS
forall a. Show a => [Prioritized a] -> ShowS
forall a. Show a => Prioritized a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Prioritized a] -> ShowS
$cshowList :: forall a. Show a => [Prioritized a] -> ShowS
show :: Prioritized a -> String
$cshow :: forall a. Show a => Prioritized a -> String
showsPrec :: Int -> Prioritized a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Prioritized a -> ShowS
Show)

instance Functor Prioritized where
  fmap :: (a -> b) -> Prioritized a -> Prioritized b
fmap a -> b
f (Prioritized Int
p a
x) = Int -> b -> Prioritized b
forall a. Int -> a -> Prioritized a
Prioritized Int
p (a -> b
f a
x)

instance Semigroup a => Semigroup (Prioritized a) where
  a1 :: Prioritized a
a1@(Prioritized Int
p1 a
x1) <> :: Prioritized a -> Prioritized a -> Prioritized a
<> a2 :: Prioritized a
a2@(Prioritized Int
p2 a
x2) = case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
p1 Int
p2 of
    Ordering
LT -> Prioritized a
a2
    Ordering
GT -> Prioritized a
a1
    Ordering
EQ -> Int -> a -> Prioritized a
forall a. Int -> a -> Prioritized a
Prioritized Int
p1 (a
x1 a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
x2)

-- |Combining two values with the same priority combines the values, otherwise it discards the value with a smaller priority.
instance Monoid a => Monoid (Prioritized a) where
  mempty :: Prioritized a
mempty = Int -> a -> Prioritized a
forall a. Int -> a -> Prioritized a
Prioritized Int
forall a. Bounded a => a
minBound a
forall a. Monoid a => a
mempty
  mappend :: Prioritized a -> Prioritized a -> Prioritized a
mappend a1 :: Prioritized a
a1@(Prioritized Int
p1 a
x1) a2 :: Prioritized a
a2@(Prioritized Int
p2 a
x2) = case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
p1 Int
p2 of
    Ordering
LT -> Prioritized a
a2
    Ordering
GT -> Prioritized a
a1
    Ordering
EQ -> Int -> a -> Prioritized a
forall a. Int -> a -> Prioritized a
Prioritized Int
p1 (a -> a -> a
forall a. Monoid a => a -> a -> a
mappend a
x1 a
x2)