packages feed

annihilator-0.1.0.0: Control/Annihilator.hs

-- | A class for Annihilators, which define a binary function '>|>' that follows
-- the mathematical properties of: absorbing element, associativity, and
-- commutativity.
module Control.Annihilator
    ( Annihilator(..)
    , (<|<)
    , aconcat
    , amappend
    , amconcat
    , avoid
    )
    where

-- | Annihilators are semigroups with annihilators, i.e. the
-- following laws should hold:
--
-- prop> ann >|> b = ann
-- prop> a >|> ann = ann
-- prop> a >|> b = b
class Annihilator a where
    -- | Annihilating element of '>|>'.
    ann :: a

    -- | Annihilating operator, returns the rightmost element if no
    -- annihilators 'ann' are present.
    (>|>) :: a -> a -> a


instance Annihilator () where
    ann = ()

    _ >|> _ = ()

instance Annihilator (Maybe a) where
    ann = Nothing

    Nothing >|> _ = Nothing
    _ >|> Nothing = Nothing
    _ >|> a = a

instance Annihilator ([] a) where
    ann = []

    [] >|> _ = []
    _ >|> [] = []
    _ >|> a = a

instance (Annihilator a, Annihilator b) => Annihilator (a, b) where
    ann = (ann, ann)

    (a1, b1) >|> (a2, b2) = (a1 >|> a2, b1 >|> b2)


-- | Flipped version of '>|>'.
(<|<) :: Annihilator a => a -> a -> a
(<|<) = flip (>|>)

-- | Annihilating concatenation.
aconcat :: (Annihilator a, Foldable t) => t a -> a
aconcat as
    | null as   = ann
    | otherwise = foldr1 (>|>) as

-- | Monadic append with the annihilating operator guarding each argument.
amappend :: (Annihilator a, Monoid a) => a -> a -> a
amappend a b = (a >|> b) `mappend` (a <|< b)

-- | Monadic concatenation with the annihilating operator guarding each argument.
amconcat :: (Annihilator a, Monoid a, Foldable t) => t a -> a
amconcat as
    | null as   = ann
    | otherwise = foldr1 amappend as

-- | Discard the argument and return 'ann'.
avoid :: Annihilator a => a -> a
avoid = const ann