monoid-extras-0.4.3: src/Data/Monoid/SemiDirectProduct.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TupleSections #-}
module Data.Monoid.SemiDirectProduct
( Semi, unSemi, tag, inject, untag, embed, quotient
) where
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid (Monoid(..))
#endif
import Data.Semigroup (Semigroup(..))
import Data.Monoid.Action
-- | The semi-direct product of monoids @s@ and @m@, which is a monoid
-- when @m@ acts on @s@. Structurally, the semi-direct product is
-- just a pair @(s,m)@. However, the monoid instance is different.
-- In particular, we have
--
-- > (s1,m1) <> (s2,m2) = (s1 <> (m1 `act` s2), m1 <> m2)
--
-- We think of the @m@ values as a "tag" decorating the @s@ values,
-- which also affect the way the @s@ values combine.
--
-- We call the monoid @m@ the quotient monoid and the monoid @s@ the
-- sub-monoid of the semi-direct product. The semi-direct product
-- @Semi s m@ is an extension of the monoid @s@ with @m@ being the
-- quotient.
newtype Semi s m = Semi { unSemi :: (s,m) }
instance (Semigroup m, Semigroup s, Action m s) => Semigroup (Semi s m) where
x <> y = Semi (xs <> (xm `act` ys), xm <> ym)
where (xs, xm) = unSemi x
(ys, ym) = unSemi y
{-# INLINE (<>) #-}
sconcat = foldr1 (<>)
{-# INLINE sconcat #-}
instance (Monoid m, Monoid s, Action m s) => Monoid (Semi s m) where
mempty = Semi (mempty, mempty)
{-# INLINE mempty #-}
#if !MIN_VERSION_base(4,11,0)
mappend x y = Semi (xs `mappend` (xm `act` ys), xm `mappend` ym)
where (xs, xm) = unSemi x
(ys, ym) = unSemi y
{-# INLINE mappend #-}
#endif
mconcat = foldr mappend mempty
{-# INLINE mconcat #-}
-- | Tag an @s@ value with an @m@ value to create an element of the
-- semi-direct product.
tag :: s -> m -> Semi s m
tag s m = Semi (s,m)
-- | The injection map, /i.e./ give an @s@ value a trivial tag.
inject :: Monoid m => s -> Semi s m
inject = Semi . (,mempty)
-- | Forget the monoidal tag. Of course, @untag . inject = id@, and
-- @untag (tag s m) = s@.
untag :: Semi s m -> s
untag = fst . unSemi
-- | Embed a "tag" value as a value of type @Semi s m@. Note that
--
-- @inject s <> embed m = tag s m@
--
-- and
--
-- @embed m <> inject s@ = tag (act m s) m@
--
-- The semi-direct product gives a split extension of @s@ by
-- @m@. This allows us to embed @m@ into the semi-direct
-- product. This is the embedding map. The quotient and embed maps
-- should satisfy the equation @quotient . embed = id@.
embed :: Monoid s => m -> Semi s m
embed = Semi . (mempty,)
-- | The quotient map, /i.e./ retrieve the monoidal tag value.
quotient :: Semi s m -> m
quotient = snd . unSemi