proarrow-0.1.0.0: src/Proarrow/Monoid.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Monoids and comonoids internal to a monoidal category: a 'Monoid' @m@ has @'mempty' :: 'Unit' '~>' m@
-- and @'mappend' :: m '**' m '~>' m@; dually a 'Comonoid' has 'counit' and 'comult'. Monoids in
-- 'Data.Kind.Type' are the Prelude monoids, and in a cartesian category every object is a comonoid.
module Proarrow.Monoid where
import Data.Kind (Constraint, Type)
import Data.Type.Nat (SNat (..), SNatI, snat)
import Prelude qualified as P
import Proarrow.Category.Instance.Bool (BOOL (..), Booleans (..))
import Proarrow.Category.Instance.Free (Elems, FREE, HasStructure (..), Lower, withLowerOb)
import Proarrow.Category.Instance.Free qualified as F
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Monoidal
( Monoidal (..)
, MonoidalProfunctor (..)
, NFold
, NFoldS
, SymMonoidal (..)
, Tensor
, UnitF
, swapInner
, (**)
, type (**!)
)
import Proarrow.Category.Monoidal.Action (Act, ActionAt, CoprodAction, MonoidalAction (..), actHom)
import Proarrow.Category.Monoidal.Closed (Closed (..), Exp)
import Proarrow.Category.Monoidal.Strength (Strong (..))
import Proarrow.Category.Monoidal.Strictified (Strictified (..), obj1)
import Proarrow.Colimit.BinaryCoproduct
( COPROD (..)
, Coprod (..)
, HasBinaryCoproducts (..)
, HasBiproducts (..)
, HasCoproducts
, codiag
)
import Proarrow.Colimit.Initial (HasInitialObject (..), HasZeroObject (..))
import Proarrow.Core (CAT, CategoryOf (..), Kind, Promonad (..), obj, (//), type (+->))
import Proarrow.Object (pattern Objs)
import Proarrow.Profunctor.Corepresentable (Corep (..))
import Proarrow.Profunctor.Instance.Constant (Constant)
import Proarrow.Profunctor.Instance.Identity (Id (..))
import Proarrow.Profunctor.Representable (Rep (..))
import Proarrow.Tools.Laws (Law (..), Laws (..), (===))
-- | A monoid object in a monoidal category: a unit and an (associative, unital) multiplication
-- for the object @m@. At @k = Type@ (with tensor @(,)@) this is the ordinary 'P.Monoid'.
type Monoid :: forall {k}. k -> Constraint
class (Monoidal k, Ob m) => Monoid (m :: k) where
mempty :: Unit ~> m
mappend :: m ** m ~> m
combine :: (Monoid m) => Unit ~> m -> Unit ~> m -> Unit ~> m
combine f g = mappend . (f ** g) . leftUnitorInv
memptyS :: (Monoid m) => '[] ~> '[m]
memptyS = Str mempty
mappendS :: (Monoid m) => '[m, m] ~> '[m]
mappendS = Str mappend
-- | A law-only marker class for monoids whose multiplication commutes:
-- @'mappend' . 'swap' = 'mappend'@.
class (Monoid m, SymMonoidal k) => CommutativeMonoid (m :: k)
instance (P.Monoid m) => Monoid (m :: Type) where
mempty () = P.mempty
mappend = P.uncurry (P.<>)
instance CommutativeMonoid ()
instance Monoid TRU where
mempty = Tru
mappend = Tru
instance CommutativeMonoid TRU
newtype GenElt x m = GenElt (x ~> m)
instance (Monoid m, Comonoid (x :: k)) => P.Semigroup (GenElt x (m :: k)) where
GenElt f <> GenElt g = GenElt (mappend . (f ** g) . comult)
instance (Monoid m, Comonoid (x :: k)) => P.Monoid (GenElt x (m :: k)) where
mempty = GenElt (mempty . counit)
instance (HasCoproducts k, Ob a) => Monoid (COPR (a :: k)) where
mempty = Coprod initiate
mappend = Coprod codiag
memptyAct :: forall {m} {c} t (a :: m) (n :: c). (MonoidalAction t, Monoid a, Ob n) => n ~> Act t a n
memptyAct = actHom @t (mempty @a) (obj @n) . unitorInv @t
mappendAct
:: forall {m} {c} t (a :: m) (n :: c). (MonoidalAction t, Monoid a, Ob n) => Act t a (Act t a n) ~> Act t a n
mappendAct = actHom @t (mappend @a) (obj @n) . multiplicatorInv @t @a @a @n
-- | A comonoid object: an object that can be discarded ('counit') and copied ('comult').
type Comonoid :: forall {k}. k -> Constraint
class (Monoidal k, Ob c) => Comonoid (c :: k) where
counit :: c ~> Unit
comult :: c ~> c ** c
-- | A law-only marker for comonoids whose comultiplication cocommutes: @'swap' . 'comult' = 'comult'@.
-- Dual to 'CommutativeMonoid'.
class (Comonoid c, SymMonoidal k) => CocommutativeComonoid (c :: k)
counitS :: (Comonoid c) => '[c] ~> '[]
counitS = Str counit
comultS :: (Comonoid c) => '[c] ~> '[c, c]
comultS = Str comult
-- | A comonoid structure on @c@ carried as a value. @Unit@ and @('**')@ are type families and so
-- cannot head a 'Comonoid' instance, yet the unit is a comonoid and, in a symmetric monoidal
-- category, so is a tensor of comonoids. 'unitComonoid' and 'tensorComonoid' say so at the value
-- level, so that 'Proarrow.Optic.MonoidalLens.withMonLens' can hand back the comonoid of a
-- composite residual (cf. 'Proarrow.Optic.Action.withAlgP', which passes algebras the same way).
type ComonoidOn :: forall {k}. k -> Type
data ComonoidOn (c :: k) = ComonoidOn {counitOn :: c ~> Unit, comultOn :: c ~> c ** c}
-- | The comonoid structure of a 'Comonoid' instance, as a value.
comonoidOn :: forall {k} (c :: k). (Comonoid c) => ComonoidOn c
comonoidOn = ComonoidOn counit comult
-- | The unit is a comonoid, via the unitor.
unitComonoid :: forall {k}. (Monoidal k) => ComonoidOn (Unit :: k)
unitComonoid = ComonoidOn id (leftUnitorInv @k @Unit)
-- | In a symmetric monoidal category the tensor of two comonoids is a comonoid: counit both
-- halves, or comultiply both halves and swap the inner pair.
tensorComonoid :: forall {k} (a :: k) b. (SymMonoidal k) => ComonoidOn a -> ComonoidOn b -> ComonoidOn (a ** b)
tensorComonoid (ComonoidOn ca@Objs ma) (ComonoidOn cb@Objs mb) =
ComonoidOn (leftUnitor @k @Unit . (ca ** cb)) (swapInner @a @a @b @b . (ma ** mb))
instance Comonoid (a :: Type) where
counit _ = ()
comult a = (a, a)
instance CocommutativeComonoid (a :: Type)
instance Comonoid '() where
counit = id
comult = id
instance CocommutativeComonoid '()
instance (Ob a) => Comonoid (a :: BOOL) where
counit = case obj @a of
Fls -> F2T
Tru -> Tru
comult = case obj @a of
Fls -> Fls
Tru -> Tru
instance (Ob a) => CocommutativeComonoid (a :: BOOL)
counitAct :: forall {m} {c} t (a :: m) (n :: c). (MonoidalAction t, Comonoid a, Ob n) => Act t a n ~> n
counitAct = unitor @t . actHom @t (counit @a) (obj @n)
comultAct
:: forall {m} {c} t (a :: m) (n :: c). (MonoidalAction t, Comonoid a, Ob n) => Act t a n ~> Act t a (Act t a n)
comultAct = multiplicator @t @a @a @n . actHom @t (comult @a) (obj @n)
-- | @'Supplies' c k@ says that every object of the category @k@ satisfies the constraint @c@,
-- e.g. @'Supplies' 'Comonoid' k@ for a category in which every object can be copied and discarded.
-- The constraint comes first (at a higher-rank kind) so that a partial application like
-- @'Supplies' 'Comonoid'@ has kind @Kind -> Constraint@ and can appear in a free category's
-- structure list ("Proarrow.Category.Instance.Free"). Instances are necessarily per-@c@ (an
-- instance variable cannot have a higher-rank kind); each follows the shape of the 'Comonoid' one.
type Supplies :: (forall j. j -> Constraint) -> Kind -> Constraint
class (forall (a :: k). (Ob a) => c a) => Supplies c k
instance (forall (a :: k). (Ob a) => Comonoid a) => Supplies Comonoid k
instance (forall (a :: k). (Ob a) => CocommutativeComonoid a) => Supplies CocommutativeComonoid k
instance (forall (a :: k). (Ob a) => Monoid a) => Supplies Monoid k
instance (forall (a :: k). (Ob a) => CommutativeMonoid a) => Supplies CommutativeMonoid k
instance (Comonoid c) => Monoid (OP c) where
mempty = Op counit
mappend = Op comult
instance (CocommutativeComonoid c) => CommutativeMonoid (OP c)
instance (Monoid c) => Comonoid (OP c) where
counit = Op mempty
comult = Op mappend
instance (CommutativeMonoid c) => CocommutativeComonoid (OP c)
instance (HasZeroObject k, HasBiproducts k, Ob (a :: k), Ob b) => P.Semigroup (Id a b) where
Id f <> Id g = Id (sum f g)
instance (HasZeroObject k, HasBiproducts k, Ob (a :: k), Ob b) => P.Monoid (Id a b) where
mempty = Id zero
instance (HasZeroObject k, HasBiproducts k, Ob (a :: k), Ob b) => CommutativeMonoid (Id a b)
instance (Monoidal k, Monoid r) => MonoidalProfunctor (Rep (Constant r) :: k +-> k) where
one = Rep mempty
Rep @x l ** Rep @y r = withOb2 @k @x @y (Rep (mappend . (l ** r)))
instance (HasCoproducts k, Ob r) => MonoidalProfunctor (Coprod (Rep (Constant r)) :: COPROD k +-> COPROD k) where
one = Coprod (Rep initiate)
Coprod @_ @_ @x (Rep l) ** Coprod @_ @_ @y (Rep r) = withObCoprod @k @x @y (Coprod (Rep (l ||| r)))
instance (Monoidal k, Comonoid r) => MonoidalProfunctor (Corep (Constant r) :: k +-> k) where
one = Corep counit
Corep @x l ** Corep @y r = withOb2 @k @x @y (Corep ((l ** r) . comult))
-- | Tensoring with a monoid, @m ** -@, is an applicative functor: the monoid's unit is @pure@ and
-- its multiplication is @<*>@. Rendered on the representable profunctor @'Rep' ('ActionAt' 'Tensor' m)@
-- (legs @a ~> m ** b@) this is a 'Proarrow.Category.Monoidal.Distributive.StrongDistributiveProfunctor',
-- the Writer applicative of the literature. (The 'Constant' instances above are the degenerate
-- case @b = Unit@.)
instance (SymMonoidal k, Monoid (m :: k)) => MonoidalProfunctor (Rep (ActionAt Tensor m) :: k +-> k) where
one = Rep (memptyAct @Tensor @m @Unit)
Rep @x2 l ** Rep @y2 r =
l // r // withOb2 @k @x2 @y2 (Rep ((mappend @m ** obj @(x2 ** y2)) . swapInner @m @x2 @m @y2 . (l ** r)))
instance
(Monoidal k, HasCoproducts k, Ob (m :: k))
=> MonoidalProfunctor (Coprod (Rep (ActionAt Tensor m)) :: COPROD k +-> COPROD k)
where
one = withOb2 @k @m @InitialObject (Coprod (Rep initiate))
Coprod (Rep @x2 l) ** Coprod (Rep @y2 r) =
withObCoprod @k @x2 @y2 (Coprod (Rep ((obj @m ** lft @k @x2 @y2) . l ||| (obj @m ** rgt @k @x2 @y2) . r)))
instance (SymMonoidal k, Ob (m :: k)) => Strong Tensor (Rep (ActionAt Tensor m) :: k +-> k) where
act @a (Rep @y p) =
p //
withOb2 @k @a @y (Rep (associator @k @m @a @y . (swap @k @a @m ** obj @y) . associatorInv @k @a @m @y . (obj @a ** p)))
instance (Monoidal k, HasCoproducts k, Monoid (m :: k)) => Strong CoprodAction (Rep (ActionAt Tensor m) :: k +-> k) where
act @(COPR a) (Rep @y p) =
p // withObCoprod @k @a @y (Rep ((obj @m ** lft @k @a @y) . memptyAct @Tensor @m @a ||| (obj @m ** rgt @k @a @y) . p))
-- | The exponential by a comonoid, @m ~~> -@, is an applicative functor (the reader applicative):
-- @pure@ discards the argument with the counit and @<*>@ duplicates it with the comultiplication.
-- Rendered on @'Rep' ('Exp' m)@ (legs @a ~> (m ~~> b)@) this is a
-- 'Proarrow.Category.Monoidal.Distributive.StrongDistributiveProfunctor', so a
-- 'Proarrow.Optic.Grate.Grate' is a 'Proarrow.Optic.Kaleidoscope.Kaleidoscope'.
instance (Closed k, SymMonoidal k, Comonoid (m :: k)) => MonoidalProfunctor (Rep (Exp m) :: k +-> k) where
one = Rep (curry @k @Unit @m (leftUnitor @k @Unit . (obj @Unit ** counit @m)))
Rep @x2 @_ @x1 l ** Rep @y2 @_ @y1 r =
l //
r //
withOb2 @k @x1 @y1
( withOb2 @k @x2 @y2
( withObExp @k @m @x2
( withObExp @k @m @y2
( Rep
( curry @k @(x1 ** y1) @m
( (apply @k @m @x2 ** apply @k @m @y2)
. swapInner @(m ~~> x2) @(m ~~> y2) @m @m
. ((l ** r) ** comult @m)
)
)
)
)
)
)
instance (Closed k, HasCoproducts k, Ob (m :: k)) => MonoidalProfunctor (Coprod (Rep (Exp m)) :: COPROD k +-> COPROD k) where
one = withObExp @k @m @InitialObject (Coprod (Rep initiate))
Coprod (Rep @x2 l) ** Coprod (Rep @y2 r) =
withObCoprod @k @x2 @y2 (Coprod (Rep ((lft @k @x2 @y2 ^^^ obj @m) . l ||| (rgt @k @x2 @y2 ^^^ obj @m) . r)))
instance (Closed k, SymMonoidal k, Ob (m :: k)) => Strong Tensor (Rep (Exp m) :: k +-> k) where
act @a (Rep @y @_ @x p) =
p //
withOb2 @k @a @x
( withOb2 @k @a @y
( withObExp @k @m @y
(Rep (curry @k @(a ** x) @m ((obj @a ** apply @k @m @y) . associator @k @a @(m ~~> y) @m . ((obj @a ** p) ** obj @m))))
)
)
instance (Closed k, HasCoproducts k, Comonoid (m :: k)) => Strong CoprodAction (Rep (Exp m) :: k +-> k) where
act @(COPR a) (Rep @y p) =
p //
withObCoprod @k @a @y
( withObExp @k @m @a
( withObExp @k @m @y
( Rep
((lft @k @a @y ^^^ obj @m) . curry @k @a @m (rightUnitor @k @a . (obj @a ** counit @m)) ||| (rgt @k @a @y ^^^ obj @m) . p)
)
)
)
-- | The free-category structure for @'Supplies' 'Monoid'@: every object gets formal 'mappend'
-- ('Join') and 'mempty' ('Sprout') generators, interpreted by 'foldStructure' through the
-- target's own supply.
instance ('[Supplies Monoid, Monoidal] `Elems` cs) => HasStructure cs (p :: CAT k) (Supplies Monoid) where
data Struct (Supplies Monoid) i o where
Join :: (Ob a) => Struct (Supplies Monoid) (a **! a) a
Sprout :: (Ob a) => Struct (Supplies Monoid) UnitF a
foldStructure @f _ (Join @a) = withLowerOb @f @a (mappend @(Lower f a))
foldStructure @f _ (Sprout @a) = withLowerOb @f @a (mempty @(Lower f a))
instance P.Show (Struct (Supplies Monoid) a b) where
showsPrec _ Join = P.showString "mappend"
showsPrec _ Sprout = P.showString "mempty"
-- | The free-category structure for @'Supplies' 'Comonoid'@, dually: formal 'comult' ('Fork') and
-- 'counit' ('Prune') generators for every object.
instance ('[Supplies Comonoid, Monoidal] `Elems` cs) => HasStructure cs (p :: CAT k) (Supplies Comonoid) where
data Struct (Supplies Comonoid) i o where
Fork :: (Ob a) => Struct (Supplies Comonoid) a (a **! a)
Prune :: (Ob a) => Struct (Supplies Comonoid) a UnitF
foldStructure @f _ (Fork @a) = withLowerOb @f @a (comult @(Lower f a))
foldStructure @f _ (Prune @a) = withLowerOb @f @a (counit @(Lower f a))
instance P.Show (Struct (Supplies Comonoid) a b) where
showsPrec _ Fork = P.showString "comult"
showsPrec _ Prune = P.showString "counit"
instance
('[Supplies Monoid, Monoidal] `Elems` cs, Ob (a :: FREE cs (p :: CAT k)))
=> Monoid (a :: FREE cs p)
where
mempty = F.St Sprout F.Nil
mappend = F.St Join F.Nil
-- | The free supply is commutative only up to interpretation ('FREE' has no equations); the marker
-- holds because every @'Proarrow.Category.Instance.Free.fold'@ of these arrows into a target lands in that target's commutative
-- monoid.
instance (Monoid (a :: FREE cs p), SymMonoidal (FREE cs p)) => CommutativeMonoid (a :: FREE cs p)
instance
('[Supplies Comonoid, Monoidal] `Elems` cs, Ob (a :: FREE cs (p :: CAT k)))
=> Comonoid (a :: FREE cs p)
where
counit = F.St Prune F.Nil
comult = F.St Fork F.Nil
instance (Comonoid (a :: FREE cs p), SymMonoidal (FREE cs p)) => CocommutativeComonoid (a :: FREE cs p)
-- | Collapse an @n@-fold tensor power of a monoid with 'mappend', bottoming out at 'mempty'.
fanIn :: forall n a. (SNatI n, Monoid a) => NFold n a ~> a
fanIn = case snat @n of
SZ -> mempty
SS @n' -> mappend @a . (obj @a ** fanIn @n' @a)
-- | Dually, build an @n@-fold tensor power of a comonoid with 'comult', bottoming out at 'counit'.
fanOut :: forall n a. (SNatI n, Comonoid a) => a ~> NFold n a
fanOut = case snat @n of
SZ -> counit
SS @n' -> (obj @a ** fanOut @n' @a) . comult @a
-- | The 'Proarrow.Category.Monoidal.Strictified.Strictified' counterpart of 'fanIn'.
fanInS :: forall n a. (SNatI n, Monoid a) => NFoldS n a ~> '[a]
fanInS =
case snat @n of
SZ -> Str mempty
SS @n' -> mappendS @a . (obj1 @a ** fanInS @n' @a)
-- | The 'Proarrow.Category.Monoidal.Strictified.Strictified' counterpart of 'fanOut'.
fanOutS :: forall n a. (SNatI n, Comonoid a) => '[a] ~> NFoldS n a
fanOutS =
case snat @n of
SZ -> Str counit
SS @n' -> (obj1 @a ** fanOutS @n' @a) . comultS @a
-- | In a category that supplies monoids, every object is one: 'mempty' is a unit for 'mappend' (up
-- to the unitors), and 'mappend' is associative (up to the associator).
instance Laws '[Monoidal, Supplies Monoid] where
laws =
[ Law "left unit" \ @a _ -> withOb2 @_ @Unit @a (leftUnitor @_ @a === mappend @a . (mempty @a ** obj @a))
, Law "right unit" \ @a _ -> withOb2 @_ @a @Unit (rightUnitor @_ @a === mappend @a . (obj @a ** mempty @a))
, Law "associativity" \ @a _ ->
withOb2 @_ @a @a P.$
mappend @a . (mappend @a ** obj @a) === mappend @a . (obj @a ** mappend @a) . associator @_ @a @a @a
]
-- | In a category that supplies comonoids, every object is one: 'counit' is a unit for 'comult', and
-- 'comult' is coassociative.
instance Laws '[Monoidal, Supplies Comonoid] where
laws =
[ Law "left counit" \ @a _ -> withOb2 @_ @Unit @a (leftUnitorInv @_ @a === (counit @a ** obj @a) . comult @a)
, Law "right counit" \ @a _ -> withOb2 @_ @a @Unit (rightUnitorInv @_ @a === (obj @a ** counit @a) . comult @a)
, Law "coassociativity" \ @a _ ->
withOb2 @_ @a @a P.$
associator @_ @a @a @a . (comult @a ** obj @a) . comult @a === (obj @a ** comult @a) . comult @a
]
-- | The supplied monoids are commutative: 'mappend' is unchanged by 'swap'.
instance Laws '[Monoidal, SymMonoidal, Supplies CommutativeMonoid] where
laws = [Law "commutativity" \ @a _ -> withOb2 @_ @a @a (mappend @a === mappend @a . swap @_ @a @a)]
-- | The supplied comonoids are cocommutative: 'comult' is unchanged by 'swap'.
instance Laws '[Monoidal, SymMonoidal, Supplies CocommutativeComonoid] where
laws = [Law "cocommutativity" \ @a _ -> withOb2 @_ @a @a (comult @a === swap @_ @a @a . comult @a)]