proarrow-0.1.0.0: src/Proarrow/Category/Monoidal/CopyDiscard.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans -Wno-unused-foralls #-}
-- | Monoidal categories in which every object carries a cocommutative comonoid (the
-- @'Supplies' 'CocommutativeComonoid' k@ superclass), with @'copy' :: a ~> a ** a@ and
-- @'discard' :: a ~> 'Unit'@ defaulting to its comult\/counit. This gives projections
-- 'fst'\/'snd' without @tensor = product@, e.g. in the biproduct categories
-- "Proarrow.Category.Instance.Mat" and "Proarrow.Category.Instance.FinRel". Unlike in
-- 'Proarrow.Category.Monoidal.Cartesian.Cartesian' (which has this class as a superclass, by Fox's
-- theorem) the comonoids need not be /natural/, so morphisms may duplicate\/delete resources
-- non-uniformly.
module Proarrow.Category.Monoidal.CopyDiscard where
import Data.Kind (Constraint, Type)
import Prelude (Applicative, ($))
import Proarrow.Category.Instance.Bool (BOOL (..))
import Proarrow.Category.Instance.Product ((:**:) (..))
import Proarrow.Category.Instance.Sub (SUBCAT, Sub (..), SubMonoidal)
import Proarrow.Category.Monoidal
( Monoidal (..)
, MonoidalProfunctor (..)
, SymMonoidal (..)
, Tensor
, leftUnitorWith
, rightUnitorWith
, swapInner
)
import Proarrow.Category.Monoidal.Strength (Strong (..))
import Proarrow.Category.Monoidal.Strictified (Strictified (..), listCase)
import Proarrow.Core (CategoryOf (..), Kind, OB, Profunctor (..), Promonad (..), obj, (\\), type (+->))
import Proarrow.Monoid (CocommutativeComonoid, Comonoid (..), Supplies)
import Proarrow.Profunctor.Instance.Constant (Constant)
import Proarrow.Profunctor.Representable (Rep (..))
import Proarrow.Tools.Laws (Equation, Law (..), Laws (..), (===))
class (SymMonoidal k, Supplies CocommutativeComonoid k) => CopyDiscard k where
copy :: (Ob (a :: k)) => a ~> a ** a
copy = comult
discard :: (Ob (a :: k)) => a ~> Unit
discard = counit
-- | The constant functor ignores the acting object: discard it. Only copying\/discarding is
-- needed, so this works in biproduct categories as well as cartesian ones.
instance (CopyDiscard k, Ob r) => Strong Tensor (Rep (Constant r) :: k +-> k) where
act @a (Rep @y p) = withOb2 @k @a @y (Rep (p . leftUnitorWith (discard @k @a))) \\ p
-- | The structures the laws of a copy-discard category are stated for.
type CopyDiscardStructures :: [Kind -> Constraint]
type CopyDiscardStructures = '[Monoidal, SymMonoidal, CopyDiscard]
-- | 'copy' and 'discard' are the supplied comonoid, and they respect the tensor: copying or
-- discarding @a '**' b@ is copying or discarding both parts, and on the unit they do nothing.
-- The comonoid laws and cocommutativity are those of the supply, in "Proarrow.Monoid".
instance Laws CopyDiscardStructures where
laws =
[ Law "copy is comult" \ @a _ -> copy @_ @a === comult @a
, Law "discard is counit" \ @a _ -> discard @_ @a === counit @a
, Law "copy of a tensor" \ @a @b _ ->
withOb2 @_ @a @b $
withOb2 @_ @a @a $
withOb2 @_ @b @b $
withOb2 @_ @(a ** b) @(a ** b) $
copy @_ @(a ** b) === swapInner @a @a @b @b . (copy @_ @a ** copy @_ @b)
, Law "discard of a tensor" \ @a @b _ ->
withOb2 @_ @a @b (discard @_ @(a ** b) === leftUnitor @_ @Unit . (discard @_ @a ** discard @_ @b))
, Law "copy of the unit" \ @a _ -> copyOfUnit @a
, Law "discard of the unit" \ @a _ -> discardOfUnit @a
]
-- | 'copy' on the unit is a unitor; @a@ only says which category.
copyOfUnit :: forall {k} (a :: k) m. (CopyDiscard k, Applicative m) => m (Equation k)
copyOfUnit = withOb2 @k @Unit @Unit (copy @k @Unit === leftUnitorInv @k @Unit)
-- | 'discard' on the unit is the identity; @a@ only says which category.
discardOfUnit :: forall {k} (a :: k) m. (CopyDiscard k, Applicative m) => m (Equation k)
discardOfUnit = discard @k @Unit === obj @Unit
copyS :: (CopyDiscard k, Ob (a :: k)) => '[a] ~> '[a, a]
copyS = Str copy
discardS :: (CopyDiscard k, Ob (a :: k)) => '[a] ~> '[]
discardS = Str discard
instance CopyDiscard Type
instance CopyDiscard ()
instance CopyDiscard BOOL
-- | The comonoid supply of a product category, a subcategory and a strictified category are
-- inherited componentwise: each object's comonoid is the ambient 'copy'\/'discard'.
instance (CopyDiscard j, CopyDiscard k, Ob (a :: (j, k))) => Comonoid (a :: (j, k)) where
counit = discard
comult = copy
instance (CopyDiscard j, CopyDiscard k, Ob (a :: (j, k))) => CocommutativeComonoid (a :: (j, k))
instance (CopyDiscard j, CopyDiscard k) => CopyDiscard (j, k) where
copy = copy :**: copy
discard = discard :**: discard
instance (SubMonoidal ob, CopyDiscard k, Ob (a :: SUBCAT ob)) => Comonoid (a :: SUBCAT (ob :: OB k)) where
counit = discard
comult = copy
instance (SubMonoidal ob, CopyDiscard k, Ob (a :: SUBCAT ob)) => CocommutativeComonoid (a :: SUBCAT (ob :: OB k))
instance (SubMonoidal ob, CopyDiscard k) => CopyDiscard (SUBCAT (ob :: OB k)) where
copy = Sub copy
discard = Sub discard
instance (CopyDiscard k, Ob (as :: [k])) => Comonoid (as :: [k]) where
counit = discard
comult = copy
instance (CopyDiscard k, Ob (as :: [k])) => CocommutativeComonoid (as :: [k])
instance (CopyDiscard k) => CopyDiscard [k] where
copy @as0 =
listCase @as0
id
(\ @a -> Str @'[a] @'[a, a] copy)
( \ @a @as ->
(obj @'[a] ** (associator @_ @as @'[a] @as . (swap @[k] @'[a] @as ** obj @as)))
. (Str @'[a] @'[a, a] copy ** copy)
)
discard @as =
listCase @as
id
(Str discard)
(\ @a -> Str @'[a] @'[] discard ** discard)
fst :: forall {k} (a :: k) b. (CopyDiscard k, Ob a, Ob b) => (a ** b) ~> a
fst = rightUnitorWith (discard @k @b)
snd :: forall {k} a (b :: k). (CopyDiscard k, Ob a, Ob b) => (a ** b) ~> b
snd = leftUnitorWith (discard @k @a)
(&&&) :: forall {k} (a :: k) x y. (CopyDiscard k) => a ~> x -> a ~> y -> a ~> x ** y
f &&& g = (f ** g) . copy \\ f