proarrow-0.1.0.0: src/Proarrow/Category/Monoidal.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | Monoidal categories, as kinds with a tensor: 'Monoidal' provides 'Unit', the tensor @('**')@,
-- and the unitor and associator isomorphisms; 'SymMonoidal' adds the symmetry 'swap'. A
-- 'MonoidalProfunctor' is a lax monoidal profunctor with 'one' and a value-level @('**')@, and a
-- category is 'Monoidal' if and only if its hom-profunctor is.
module Proarrow.Category.Monoidal where
import Data.Kind (Constraint)
import Data.Type.Nat (Nat (..), SNat (..), SNatI, snat)
import Prelude (Show, ($), type (~))
import Prelude qualified as P
import Proarrow.Category.Instance.Free
( Elem (..)
, Elems
, FREE (..)
, Free (..)
, HasStructure (..)
, IsFreeOb (..)
, Lower
, WithShow
, withLowerOb
)
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Instance.Product (Fst, Snd, (:**:) (..))
import Proarrow.Category.Instance.Unit qualified as U
import Proarrow.Core
( CAT
, CategoryOf (..)
, Kind
, Obj
, Profunctor (..)
, Promonad (..)
, UN
, obj
, src
, tgt
, type (+->)
)
import Proarrow.Functor (FunctorForRep (..))
import Proarrow.Optic (PIso, iso)
import Proarrow.Profunctor.Corepresentable (Corepresentable (..), corepUniv)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Identity qualified as Id
import Proarrow.Profunctor.Representable (CorepStar, Rep, RepCostar, Representable (..), repUniv)
import Proarrow.Tools.Laws (Inverses (..), Law (..), Laws (..), ProLaw (..), ProLaws (..), inverses, (=:=), (===))
infixl 8 **
infixl 7 ==
-- This is equal to a lax monoidal functor for representable profunctors
-- and to an oplax monoidal functor for corepresentable profunctors.
type MonoidalProfunctor :: forall {j} {k}. j +-> k -> Constraint
class (Monoidal j, Monoidal k, Profunctor p) => MonoidalProfunctor (p :: j +-> k) where
one :: p Unit Unit
(**) :: p x1 x2 -> p y1 y2 -> p (x1 ** y1) (x2 ** y2)
instance MonoidalProfunctor U.Unit where
one = U.Unit
U.Unit ** U.Unit = U.Unit
instance (MonoidalProfunctor p, MonoidalProfunctor q) => MonoidalProfunctor (p :**: q) where
one = one :**: one
(f1 :**: f2) ** (g1 :**: g2) = (f1 ** g1) :**: (f2 ** g2)
instance (Monoidal k) => MonoidalProfunctor (Id.Id :: k +-> k) where
one = Id.Id one
Id.Id f ** Id.Id g = Id.Id (f ** g)
instance (MonoidalProfunctor p, MonoidalProfunctor q) => MonoidalProfunctor (p :.: q) where
one = one :.: one
(p :.: q) ** (r :.: s) = (p ** r) :.: (q ** s)
-- | A representable profunctor that is a 'MonoidalProfunctor': its functor @p '%'@ is /lax/
-- monoidal, splitting as 'par0Rep' and 'parRep'.
type LaxMonoidal p = (MonoidalProfunctor p, Representable p)
par0Rep :: (LaxMonoidal p) => Unit ~> p % Unit
par0Rep @p = index @p one
parRep :: (LaxMonoidal p, Ob x, Ob y) => (p % x) ** (p % y) ~> p % (x ** y)
parRep @p @x @y = index @p (repUniv @p @x ** repUniv @p @y)
-- | A corepresentable profunctor that is a 'MonoidalProfunctor': its functor @p '%%'@ is /oplax/
-- monoidal, splitting as 'unpar0Corep' and 'unparCorep'.
type OplaxMonoidal p = (MonoidalProfunctor p, Corepresentable p)
unpar0Corep :: (OplaxMonoidal p) => p %% Unit ~> Unit
unpar0Corep @p = coindex @p one
unparCorep :: (OplaxMonoidal p, Ob x, Ob y) => p %% (x ** y) ~> (p %% x) ** (p %% y)
unparCorep @p @x @y = coindex @p (corepUniv @p @x ** corepUniv @p @y)
-- | A __representable__ profunctor whose functor @p '%'@ is /oplax/ monoidal. Stating the oplax
-- structure of a representable functor means naming that same functor in its other variance, as
-- 'RepCostar' does. So the postfix here says which presentation @p@ is in, not which structure it
-- carries. Weaker than 'StrongMonoidalRep', which additionally asks @p@ itself to be
-- 'LaxMonoidal'.
type OplaxMonoidalRep p = (Representable p, OplaxMonoidal (RepCostar p))
unpar0Rep :: (OplaxMonoidalRep p) => p % Unit ~> Unit
unpar0Rep @p = unpar0Corep @(RepCostar p)
unparRep :: (OplaxMonoidalRep p, Ob x, Ob y) => p % (x ** y) ~> (p % x) ** (p % y)
unparRep @p @x @y = unparCorep @(RepCostar p) @x @y
-- | A __corepresentable__ profunctor whose functor @p '%%'@ is /lax/ monoidal, dually through
-- 'CorepStar'.
type LaxMonoidalCorep p = (Corepresentable p, LaxMonoidal (CorepStar p))
par0Corep :: (LaxMonoidalCorep p) => Unit ~> p %% Unit
par0Corep @p = par0Rep @(CorepStar p)
parCorep :: (LaxMonoidalCorep p, Ob x, Ob y) => (p %% x) ** (p %% y) ~> p %% (x ** y)
parCorep @p @x @y = parRep @(CorepStar p) @x @y
-- | A representable profunctor whose functor is /strong/ monoidal: lax as it stands, and oplax in
-- its other variance.
type StrongMonoidalRep p = (LaxMonoidal p, OplaxMonoidalRep p)
-- | A corepresentable profunctor whose functor is /strong/ monoidal, dually.
type StrongMonoidalCorep p = (OplaxMonoidal p, LaxMonoidalCorep p)
-- | A monoidal category: a tensor @'**'@ with a 'Unit', associative and unital up to the coherent
-- isomorphisms below. The tensor's action on arrows is the 'MonoidalProfunctor' method @**@ at
-- @('~>')@, which the superclass supplies.
--
-- __Laws:__
--
-- The three isomorphisms must be mutually inverse:
--
-- * @'leftUnitor' . 'leftUnitorInv' = 'id'@ and @'leftUnitorInv' . 'leftUnitor' = 'id'@
-- * @'rightUnitor' . 'rightUnitorInv' = 'id'@ and @'rightUnitorInv' . 'rightUnitor' = 'id'@
-- * @'associator' . 'associatorInv' = 'id'@ and @'associatorInv' . 'associator' = 'id'@
--
-- and natural in every argument:
--
-- * @'leftUnitor' . ('id' '**' f) = f . 'leftUnitor'@
-- * @'rightUnitor' . (f '**' 'id') = f . 'rightUnitor'@
-- * @'associator' . ((f '**' g) '**' h) = (f '**' (g '**' h)) . 'associator'@
--
-- subject to the two coherence conditions:
--
-- * Triangle: @('id' '**' 'leftUnitor') . 'associator' = 'rightUnitor' '**' 'id'@
-- * Pentagon: @('id' '**' 'associator') . 'associator' . ('associator' '**' 'id')
-- = 'associator' . 'associator'@
--
-- Checked by 'Proarrow.Testing.Laws.testMonoidal'.
type Monoidal :: Kind -> Constraint
class (CategoryOf k, MonoidalProfunctor ((~>) :: CAT k), Ob (Unit :: k)) => Monoidal k where
-- | The tensor unit.
type Unit :: k
-- | The tensor product of two objects.
type (a :: k) ** (b :: k) :: k
-- | Recovers @'Ob' (a '**' b)@ from the objecthood of the factors.
withOb2 :: (Ob (a :: k), Ob b) => ((Ob (a ** b)) => r) -> r
-- | Cancels a 'Unit' on the left.
leftUnitor :: (Ob (a :: k)) => Unit ** a ~> a
default leftUnitor :: (Ob (a :: k), (Unit ** a) ~ a) => Unit ** a ~> a
leftUnitor = id
-- | Introduces a 'Unit' on the left; inverse to 'leftUnitor'.
leftUnitorInv :: (Ob (a :: k)) => a ~> Unit ** a
default leftUnitorInv :: (Ob (a :: k), (Unit ** a) ~ a) => a ~> Unit ** a
leftUnitorInv = id
-- | Cancels a 'Unit' on the right.
rightUnitor :: (Ob (a :: k)) => a ** Unit ~> a
default rightUnitor :: (Ob (a :: k), (a ** Unit) ~ a) => a ** Unit ~> a
rightUnitor = id
-- | Introduces a 'Unit' on the right; inverse to 'rightUnitor'.
rightUnitorInv :: (Ob (a :: k)) => a ~> a ** Unit
default rightUnitorInv :: (Ob (a :: k), (a ** Unit) ~ a) => a ~> a ** Unit
rightUnitorInv = id
-- | Reassociates the tensor to the right.
associator :: (Ob (a :: k), Ob b, Ob c) => (a ** b) ** c ~> a ** (b ** c)
-- | Reassociates the tensor to the left; inverse to 'associator'.
associatorInv :: (Ob (a :: k), Ob b, Ob c) => a ** (b ** c) ~> (a ** b) ** c
leftUnitorIso :: (Monoidal k, Ob (a :: k), Ob (a' :: k)) => PIso (Unit ** a) (Unit ** a') a a'
leftUnitorIso = iso leftUnitor leftUnitorInv
rightUnitorIso :: (Monoidal k, Ob (a :: k), Ob (a' :: k)) => PIso (a ** Unit) (a' ** Unit) a a'
rightUnitorIso = iso rightUnitor rightUnitorInv
associatorIso
:: (Monoidal k, Ob (a :: k), Ob b, Ob c, Ob (a' :: k), Ob b', Ob c')
=> PIso ((a ** b) ** c) ((a' ** b') ** c') (a ** (b ** c)) (a' ** (b' ** c'))
associatorIso @k @a @b @c @a' @b' @c' = iso (associator @k @a @b @c) (associatorInv @k @a' @b' @c')
class (((a ** b) ** c) ~ (a ** (b ** c))) => StrictlyAssoc a b c
instance (((a ** b) ** c) ~ (a ** (b ** c))) => StrictlyAssoc a b c
-- | The @n@-fold tensor power of @x@: @x '**' x '**' … '**' x@, @n@ times, terminated by 'Unit'.
type family NFold (n :: Nat) (x :: k) :: k where
NFold Z x = Unit
NFold (S n) x = x ** NFold n x
-- | The 'Proarrow.Category.Monoidal.Strictified.Strictified' counterpart of 'NFold': @n@ copies
-- of @x@ as a list, rather than nested tensors.
type family NFoldS (n :: Nat) (x :: k) :: [k] where
NFoldS Z x = '[]
NFoldS (S n) x = x ': NFoldS n x
-- | @'NFold' n a@ is an object whenever @a@ is.
withObNFold :: forall {k} n (a :: k) r. (SNatI n, Ob a, Monoidal k) => ((Ob (NFold n a)) => r) -> r
withObNFold r = case snat @n of
SZ -> r
SS @n' -> withObNFold @n' @a (withOb2 @k @a @(NFold n' a) r)
-- | If your monoidal category is a strict monoidal category, add 'Strictly' to your 'Ob' constraint.
-- This will let GHC know that the unitors and associators are strict, so you won't have to provide proof of that.
--
-- The four unitors then default to 'id'. The defaults need only @'Unit' '**' a ~ a@ and
-- @a '**' 'Unit' ~ a@, so they also fire for a strictly unital category such as
-- 'Proarrow.Category.Instance.Mat.MatK' or 'Proarrow.Category.Instance.ZX.ZX'. Both associators
-- can use 'associatorDefault':
--
-- @
-- associator \@a \@b \@c = associatorDefault \@a \@b \@c
-- associatorInv \@a \@b \@c = associatorDefault \@a \@b \@c
-- @
type Strictly :: forall {k}. k -> Constraint
class (a ** Unit ~ a, Unit ** a ~ a, forall b c. (Ob b, Ob c) => StrictlyAssoc a b c) => Strictly (a :: k) where
associatorDefault :: forall b c. (Monoidal k, Ob a, Ob b, Ob c) => (a ** b) ** c ~> a ** (b ** c)
instance (a ** Unit ~ a, Unit ** a ~ a, forall b c. (Ob b, Ob c) => StrictlyAssoc a b c) => Strictly (a :: k) where
associatorDefault @b @c = withOb2 @_ @b @c (withOb2 @_ @a @(b ** c) id)
instance Monoidal () where
type Unit = '()
-- a wildcard, not @'()@, so that @a ** b@ reduces for an abstract @a@, as on pairs
type _ ** _ = '()
withOb2 @'() @'() r = r
leftUnitor = U.Unit
leftUnitorInv = U.Unit
rightUnitor = U.Unit
rightUnitorInv = U.Unit
associator = U.Unit
associatorInv = U.Unit
instance (Monoidal j, Monoidal k) => Monoidal (j, k) where
type Unit = '(Unit, Unit)
-- Through the projections rather than by matching the pair, so that @a ** b@ reduces for an
-- abstract @a@: the quantified @a ** b ~ a && b@ of 'Proarrow.Category.Monoidal.Cartesian.Cartesian'
-- needs that.
type a ** b = '(Fst @ a ** Fst @ b, Snd @ a ** Snd @ b)
withOb2 @'(a1, a2) @'(b1, b2) r = withOb2 @j @a1 @b1 (withOb2 @k @a2 @b2 r)
leftUnitor @'(a1, a2) = leftUnitor @j @a1 :**: leftUnitor @k @a2
leftUnitorInv @'(a1, a2) = leftUnitorInv @j @a1 :**: leftUnitorInv @k @a2
rightUnitor @'(a1, a2) = rightUnitor @j @a1 :**: rightUnitor @k @a2
rightUnitorInv @'(a1, a2) = rightUnitorInv @j @a1 :**: rightUnitorInv @k @a2
associator @'(a1, a2) @'(b1, b2) @'(c1, c2) = associator @j @a1 @b1 @c1 :**: associator @k @a2 @b2 @c2
associatorInv @'(a1, a2) @'(b1, b2) @'(c1, c2) = associatorInv @j @a1 @b1 @c1 :**: associatorInv @k @a2 @b2 @c2
instance (MonoidalProfunctor p) => MonoidalProfunctor (Op p) where
one = Op one
Op l ** Op r = Op (l ** r)
-- | The opposite of a monoidal category is also monoidal, with the same tensor product.
instance (Monoidal k) => Monoidal (OPPOSITE k) where
type Unit = OP Unit
type a ** b = OP (UN OP a ** UN OP b)
withOb2 @(OP a) @(OP b) r = withOb2 @k @a @b r
leftUnitor = Op leftUnitorInv
leftUnitorInv = Op leftUnitor
rightUnitor = Op rightUnitorInv
rightUnitorInv = Op rightUnitor
associator @(OP a) @(OP b) @(OP c) = Op (associatorInv @k @a @b @c)
associatorInv @(OP a) @(OP b) @(OP c) = Op (associator @k @a @b @c)
instance (SymMonoidal k) => SymMonoidal (OPPOSITE k) where
swap @(OP a) @(OP b) = Op (swap @k @b @a)
(==) :: (CategoryOf k) => (a :: k) ~> b -> b ~> c -> a ~> c
f == g = g . f
obj2 :: forall {k} a b. (Monoidal k, Ob (a :: k), Ob b) => Obj (a ** b)
obj2 = obj @a ** obj @b
leftUnitor' :: (Monoidal k) => (a :: k) ~> b -> Unit ** a ~> b
leftUnitor' f = f . leftUnitor \\ f
leftUnitorInv' :: (Monoidal k) => (a :: k) ~> b -> a ~> Unit ** b
leftUnitorInv' f = leftUnitorInv . f \\ f
rightUnitor' :: (Monoidal k) => (a :: k) ~> b -> a ** Unit ~> b
rightUnitor' f = f . rightUnitor \\ f
rightUnitorInv' :: (Monoidal k) => (a :: k) ~> b -> a ~> b ** Unit
rightUnitorInv' f = rightUnitorInv . f \\ f
associator' :: forall {k} a b c. (Monoidal k) => Obj (a :: k) -> Obj b -> Obj c -> (a ** b) ** c ~> a ** (b ** c)
associator' a b c = associator @k @a @b @c \\ a \\ b \\ c
associatorInv' :: forall {k} a b c. (Monoidal k) => Obj (a :: k) -> Obj b -> Obj c -> a ** (b ** c) ~> (a ** b) ** c
associatorInv' a b c = associatorInv @k @a @b @c \\ a \\ b \\ c
leftUnitorWith :: forall {k} a b. (Monoidal k, Ob (a :: k)) => b ~> Unit -> b ** a ~> a
leftUnitorWith f = leftUnitor . (f ** obj @a)
leftUnitorInvWith :: forall {k} a b. (Monoidal k, Ob (a :: k)) => Unit ~> b -> a ~> b ** a
leftUnitorInvWith f = (f ** obj @a) . leftUnitorInv
rightUnitorWith :: forall {k} a b. (Monoidal k, Ob (a :: k)) => b ~> Unit -> a ** b ~> a
rightUnitorWith f = rightUnitor . (obj @a ** f)
rightUnitorInvWith :: forall {k} a b. (Monoidal k, Ob (a :: k)) => Unit ~> b -> a ~> a ** b
rightUnitorInvWith f = (obj @a ** f) . rightUnitorInv
unitObj :: (Monoidal k) => Obj (Unit :: k)
unitObj = one
first :: forall {k} c a b. (Monoidal k, Ob (c :: k)) => (a ~> b) -> (a ** c) ~> (b ** c)
first f = f ** obj @c
second :: forall {k} c a b. (Monoidal k, Ob (c :: k)) => (a ~> b) -> (c ** a) ~> (c ** b)
second f = obj @c ** f
type State a = Unit ~> a
type Costate a = a ~> Unit
type Scalar k = (Unit :: k) ~> Unit
class (Monoidal k) => SymMonoidal k where
swap :: (Ob (a :: k), Ob b) => (a ** b) ~> (b ** a)
instance SymMonoidal () where
swap = U.Unit
instance (SymMonoidal j, SymMonoidal k) => SymMonoidal (j, k) where
swap @'(a1, a2) @'(b1, b2) = swap @j @a1 @b1 :**: swap @k @a2 @b2
swap' :: forall {k} (a :: k) a' b b'. (SymMonoidal k) => a ~> a' -> b ~> b' -> (a ** b) ~> (b' ** a')
swap' f g = swap @k @a' @b' . (f ** g) \\ f \\ g
swapInner'
:: (SymMonoidal k)
=> (a :: k) ~> a'
-> b ~> b'
-> c ~> c'
-> d ~> d'
-> ((a ** b) ** (c ** d)) ~> ((a' ** c') ** (b' ** d'))
swapInner' a b c d =
associatorInv' (tgt a) (tgt c) (tgt b ** tgt d)
. (a ** (associator' (tgt c) (tgt b) (tgt d) . (swap' b c ** d) . associatorInv' (src b) (src c) (src d)))
. associator' (src a) (src b) (src c ** src d)
swapInner
:: forall {k} a b c d. (SymMonoidal k, Ob (a :: k), Ob b, Ob c, Ob d) => ((a ** b) ** (c ** d)) ~> ((a ** c) ** (b ** d))
swapInner =
withOb2 @k @b @d $
withOb2 @k @c @d $
associatorInv @k @a @c @(b ** d)
. (obj @a ** (associator @k @c @b @d . (swap @k @b @c ** obj @d) . associatorInv @k @b @c @d))
. associator @k @a @b @(c ** d)
swapFst
:: forall {k} (a :: k) b c d. (SymMonoidal k, Ob a, Ob b, Ob c, Ob d) => (a ** b) ** (c ** d) ~> (c ** b) ** (a ** d)
swapFst = (swap @k @b @c ** obj2 @a @d) . swapInner @b @a @c @d . (swap @k @a @b ** obj2 @c @d)
swapSnd
:: forall {k} a (b :: k) c d. (SymMonoidal k, Ob a, Ob b, Ob c, Ob d) => (a ** b) ** (c ** d) ~> (a ** d) ** (c ** b)
swapSnd = (obj2 @a @d ** swap @k @b @c) . swapInner @a @b @d @c . (obj2 @a @b ** swap @k @c @d)
swapOuter
:: forall {k} a b c d. (SymMonoidal k, Ob (a :: k), Ob b, Ob c, Ob d) => ((a ** b) ** (c ** d)) ~> ((d ** b) ** (c ** a))
swapOuter = (obj2 @d @b ** swap @k @a @c) . swapFst @a @b @d @c . (obj2 @a @b ** swap @k @c @d)
data UnitRep :: () +-> k
instance (Monoidal k) => FunctorForRep (UnitRep :: () +-> k) where
type UnitRep @ '() = Unit
fmap U.Unit = unitObj
data MultRep :: (k, k) +-> k
instance (Monoidal k) => FunctorForRep (MultRep :: (k, k) +-> k) where
type MultRep @ '(a, b) = a ** b
fmap (f :**: g) = f ** g
type Tensor = Rep MultRep
data family UnitF :: k
instance (Monoidal `Elem` cs) => IsFreeOb (UnitF :: FREE cs p) where
type Lower f UnitF = Unit
lowerOb @k' @_ r = fromAll @Monoidal @cs @k' r
data family (**!) (a :: k) (b :: k) :: k
instance (IsFreeOb (a :: FREE cs p), IsFreeOb b, Monoidal `Elem` cs) => IsFreeOb (a **! b) where
type Lower f (a **! b) = Lower f a ** Lower f b
lowerOb @k' @f r = fromAll @Monoidal @cs @k' (withLowerOb @f @a (withLowerOb @f @b (withOb2 @k' @(Lower f a) @(Lower f b) r)))
instance (Monoidal `Elem` cs) => HasStructure cs (p :: CAT k) Monoidal where
data Struct Monoidal i o where
Par0 :: Struct Monoidal UnitF UnitF
Par :: a ~> b -> c ~> d -> Struct Monoidal (a **! c) (b **! d)
LeftUnitor :: (Ob a) => Struct Monoidal (UnitF **! a) a
LeftUnitorInv :: (Ob a) => Struct Monoidal a (UnitF **! a)
RightUnitor :: (Ob a) => Struct Monoidal (a **! UnitF) a
RightUnitorInv :: (Ob a) => Struct Monoidal a (a **! UnitF)
Associator :: (Ob a, Ob b, Ob c) => Struct Monoidal ((a **! b) **! c) (a **! (b **! c))
AssociatorInv :: (Ob a, Ob b, Ob c) => Struct Monoidal (a **! (b **! c)) ((a **! b) **! c)
foldStructure _ Par0 = one
foldStructure go (Par f g) = go f ** go g
foldStructure @f _ (LeftUnitor @a) = withLowerOb @f @a leftUnitor
foldStructure @f _ (LeftUnitorInv @a) = withLowerOb @f @a leftUnitorInv
foldStructure @f _ (RightUnitor @a) = withLowerOb @f @a rightUnitor
foldStructure @f _ (RightUnitorInv @a) = withLowerOb @f @a rightUnitorInv
foldStructure @f _ (Associator @a @b @c') = withLowerOb @f @a (withLowerOb @f @b (withLowerOb @f @c' (associator @_ @(Lower f a) @(Lower f b) @(Lower f c'))))
foldStructure @f _ (AssociatorInv @a @b @c') = withLowerOb @f @a (withLowerOb @f @b (withLowerOb @f @c' (associatorInv @_ @(Lower f a) @(Lower f b) @(Lower f c'))))
instance (WithShow a) => Show (Struct Monoidal a b) where
showsPrec _ Par0 = P.showString "one"
showsPrec d (Par f g) = P.showParen (d P.> 8) $ P.showsPrec 9 f . P.showString " ** " . P.showsPrec 9 g
showsPrec _ LeftUnitor = P.showString "leftUnitor"
showsPrec _ LeftUnitorInv = P.showString "leftUnitorInv"
showsPrec _ RightUnitor = P.showString "rightUnitor"
showsPrec _ RightUnitorInv = P.showString "rightUnitorInv"
showsPrec _ Associator = P.showString "associator"
showsPrec _ AssociatorInv = P.showString "associatorInv"
instance (Monoidal `Elem` cs) => MonoidalProfunctor (Free :: CAT (FREE cs (p :: CAT k))) where
one = St Par0 Nil
f ** g = St (Par f g) Nil \\ f \\ g
instance (Monoidal `Elem` cs) => Monoidal (FREE cs (p :: CAT k)) where
type Unit = UnitF
type a ** b = a **! b
withOb2 r = r
leftUnitor = St LeftUnitor Nil
leftUnitorInv = St LeftUnitorInv Nil
rightUnitor = St RightUnitor Nil
rightUnitorInv = St RightUnitorInv Nil
associator = St Associator Nil
associatorInv = St AssociatorInv Nil
-- | The structures the free category needs for 'SymMonoidal', and those its laws are stated for.
type SymMonoidalStructures :: [Kind -> Constraint]
type SymMonoidalStructures = '[Monoidal, SymMonoidal]
instance (SymMonoidalStructures `Elems` cs) => HasStructure cs (p :: CAT k) SymMonoidal where
data Struct SymMonoidal i o where
Swap :: (Ob a, Ob b) => Struct SymMonoidal (a **! b) (b **! a)
foldStructure @f _ (Swap @a @b) = withLowerOb @f @a (withLowerOb @f @b (swap @_ @(Lower f a) @(Lower f b)))
instance Show (Struct SymMonoidal a b) where
showsPrec _ Swap = P.showString "swap"
instance (SymMonoidalStructures `Elems` cs) => SymMonoidal (FREE cs (p :: CAT k)) where
swap = St Swap Nil
-- | The tensor is a bifunctor, and the unitors and the associator are natural isomorphisms
-- satisfying the triangle and pentagon identities.
instance Laws '[Monoidal] where
laws =
inverses "leftUnitor" (\ @a -> Inverses (leftUnitor @_ @a) (leftUnitorInv @_ @a))
P.++ inverses "rightUnitor" (\ @a -> Inverses (rightUnitor @_ @a) (rightUnitorInv @_ @a))
P.++ inverses
"associator"
(\ @a @b @c -> Inverses (associator @_ @a @b @c) (associatorInv @_ @a @b @c))
P.++ [ Law "tensor identity" \ @a @b _ -> withOb2 @_ @a @b (obj @a ** obj @b === id)
, Law "tensor interchange" \ @a @b @c @d @e mor -> do
f <- mor @a @b "f"
g <- mor @b @c "g"
h <- mor @d @e "h"
i <- mor @e @c "i"
(g . f) ** (i . h) === (g ** i) . (f ** h)
, Law "leftUnitor naturality" \ @a @b mor -> do
f <- mor @a @b "f"
leftUnitor @_ @b . (one ** f) === f . leftUnitor @_ @a
, Law "leftUnitorInv naturality" \ @a @b mor -> do
f <- mor @a @b "f"
leftUnitorInv @_ @b . f === (one ** f) . leftUnitorInv @_ @a
, Law "rightUnitor naturality" \ @a @b mor -> do
f <- mor @a @b "f"
rightUnitor @_ @b . (f ** one) === f . rightUnitor @_ @a
, Law "rightUnitorInv naturality" \ @a @b mor -> do
f <- mor @a @b "f"
rightUnitorInv @_ @b . f === (f ** one) . rightUnitorInv @_ @a
, Law "associator naturality" \ @a @b @c @d mor -> do
f <- mor @a @b "f"
g <- mor @b @c "g"
h <- mor @c @d "h"
associator @_ @b @c @d . ((f ** g) ** h) === (f ** (g ** h)) . associator @_ @a @b @c
, Law "associatorInv naturality" \ @a @b @c @d mor -> do
f <- mor @a @b "f"
g <- mor @b @c "g"
h <- mor @c @d "h"
associatorInv @_ @b @c @d . (f ** (g ** h)) === ((f ** g) ** h) . associatorInv @_ @a @b @c
, Law "triangle identity" \ @a @b _ ->
(obj @a ** leftUnitor @_ @b) . associator @_ @a @Unit @b === rightUnitor @_ @a ** obj @b
, Law "pentagon identity" \ @a @b @c @d _ ->
withOb2 @_ @a @b $
withOb2 @_ @b @c $
withOb2 @_ @c @d $
(obj @a ** associator @_ @b @c @d)
. associator @_ @a @(b ** c) @d
. (associator @_ @a @b @c ** obj @d)
=== associator @_ @a @b @(c ** d)
. associator @_ @(a ** b) @c @d
]
-- | 'swap' is a natural self-inverse satisfying the hexagon identity.
instance Laws SymMonoidalStructures where
laws =
[ Law "swap self-inverse" \ @a @b _ -> (swap @_ @b @a . swap @_ @a @b === id) \\ swap @_ @a @b
, Law "swap naturality" \ @a @b @c @d mor -> do
f <- mor @a @c "f"
g <- mor @b @d "g"
swap @_ @c @d . (f ** g) === (g ** f) . swap @_ @a @b
, Law "hexagon identity" \ @a @b @c _ ->
withOb2 @_ @b @c $
associator @_ @b @c @a
. swap @_ @a @(b ** c)
. associator @_ @a @b @c
=== (obj @b ** swap @_ @a @c)
. associator @_ @b @a @c
. (swap @_ @a @b ** obj @c)
]
-- | 'one' is a unit for '**' up to the unitors, '**' is associative up to the associators, and
-- '**' is natural.
instance ProLaws MonoidalProfunctor where
proLaws =
[ ProLaw "left unit" \ @_ @a @b p _ _ -> p =:= dimap (leftUnitorInv @_ @a) (leftUnitor @_ @b) (one ** p)
, ProLaw "right unit" \ @_ @a @b p _ _ -> p =:= dimap (rightUnitorInv @_ @a) (rightUnitor @_ @b) (p ** one)
, ProLaw3 "associativity" \ @_ @a @b @c @d @e @f p p' p'' _ _ ->
(p ** p') ** p'' =:= dimap (associator @_ @a @c @e) (associatorInv @_ @b @d @f) (p ** (p' ** p''))
, ProLaw3 "** naturality" \ @_ @a @b @c @d @e @f p p' _ morK morJ -> do
g <- morK @e @a "g"
g' <- morK @e @c "g'"
h <- morJ @b @f "h"
h' <- morJ @d @f "h'"
dimap (g ** g') (h ** h') (p ** p') =:= dimap g h p ** dimap g' h' p'
]