packages feed

proarrow-0.1.0.0: src/Proarrow/Category/Monoidal/CompactClosed.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# OPTIONS_GHC -Wno-unused-foralls #-}

-- | Compact closed categories: star-autonomous categories whose dual distributes over the tensor
-- ('distribDual', 'dualUnit'), so that every object has a duality unit and counit ('dualityUnit',
-- 'dualityCounit') and every morphism @x ** u ~> y ** u@ has a trace ('traceCC').
module Proarrow.Category.Monoidal.CompactClosed where

import Data.Kind (Constraint)
import Prelude (($))
import Prelude qualified as P

import Proarrow.Category.Instance.Free (Elems, FREE (..), Free (..), HasStructure (..), Lower, withLowerOb)
import Proarrow.Category.Instance.Product ((:**:) (..))
import Proarrow.Category.Instance.Unit qualified as U
import Proarrow.Category.Monoidal
  ( Monoidal (..)
  , MonoidalProfunctor (..)
  , SymMonoidal (..)
  , UnitF
  , leftUnitorWith
  , swap
  , unitObj
  , type (**!)
  )
import Proarrow.Category.Monoidal.Action (Act, MonoidalAction (..), actHom)
import Proarrow.Category.Monoidal.Closed (Closed)
import Proarrow.Category.Monoidal.StarAutonomous
  ( DualF
  , StarAutonomous (..)
  , doubleNeg
  , dualObj
  , dualityCounitSA
  , dualityUnitSA
  )
import Proarrow.Category.Monoidal.Strictified (Strictified (..), obj1, swap2, (==))
import Proarrow.Core (CAT, CategoryOf (..), Kind, Profunctor (..), Promonad (..), obj, type (+->))
import Proarrow.Tools.Laws (Inverses (..), Labelled (..), Law (..), Laws (..), inverses, (===))

class (StarAutonomous k, SymMonoidal k) => CompactClosed k where
  distribDual :: forall (a :: k) b. (Ob a, Ob b) => Dual (a ** b) ~> Dual a ** Dual b
  dualUnit :: Dual (Unit :: k) ~> Unit

  -- | The unit of the duality between @a@ and its dual. 'dualityUnitDefault' gives it from the
  -- *-autonomous structure; an instance with cups of its own can use them. (There is no default
  -- method: @a@ occurs only under type families, so GHC could not instantiate one.)
  dualityUnit :: (Ob (a :: k)) => Unit ~> a ** Dual a

  -- | The counit of the duality between @a@ and its dual; see 'dualityCounitDefault'.
  dualityCounit :: (Ob (a :: k)) => Dual a ** a ~> Unit

dualUnitInv :: forall {k}. (CompactClosed k) => (Unit :: k) ~> Dual Unit
dualUnitInv = leftUnitor @k @(Dual Unit) . dualityUnit @k @Unit \\ dualObj @(Unit :: k)

-- | 'dualityUnit' from the *-autonomous structure.
dualityUnitDefault :: forall {k} (a :: k). (CompactClosed k, Ob a) => Unit ~> a ** Dual a
dualityUnitDefault = let dualA = dualObj @a in (doubleNeg @k @a ** dualA) . distribDual @k @(Dual a) @a . dualityUnitSA @a \\ dualA

dualityUnitS :: forall {k} (a :: k). (CompactClosed k, Ob a) => '[] ~> [a, Dual a]
dualityUnitS = withObDual @k @a (Str @'[] @[a, Dual a] (dualityUnit @k @a))

-- | 'dualityCounit' from the *-autonomous structure.
dualityCounitDefault :: forall {k} (a :: k). (CompactClosed k, Ob a) => Dual a ** a ~> Unit
dualityCounitDefault = dualUnit . dualityCounitSA @a

dualityCounitS :: forall {k} (a :: k). (CompactClosed k, Ob a) => [Dual a, a] ~> '[]
dualityCounitS = withObDual @k @a (Str @[Dual a, a] @'[] (dualityCounit @k @a))

combineDual :: forall {k} a b. (CompactClosed k, Ob (a :: k), Ob b) => Dual a ** Dual b ~> Dual (a ** b)
combineDual =
  withObDual @k @a $
    withObDual @k @b $
      withOb2 @k @(Dual a) @(Dual b) $
        linDist @k @_ @a @b $
          leftUnitorWith (dualityCounit @k @a . swap @k @a @(Dual a))
            . associatorInv @k @a @(Dual a) @(Dual b)
            . swap @k @(Dual a ** Dual b) @a

combineDualS :: forall {k} a b. (CompactClosed k, Ob (a :: k), Ob b) => '[Dual a, Dual b] ~> '[Dual (a ** b)]
combineDualS =
  withObDual @k @a (withObDual @k @b (withOb2 @k @a @b (withObDual @k @(a ** b) (Str (combineDual @a @b)))))

-- | The dimension of @a@: the trace of its identity, as a scalar.
dimension :: forall {k} (a :: k). (CompactClosed k, Ob a) => (Unit :: k) ~> Unit
dimension = traceCC @a (unitObj ** obj @a)

traceCCS :: forall {k} u (x :: k) y. (CompactClosed k, Ob x, Ob y, Ob u) => [x, u] ~> [y, u] -> '[x] ~> '[y]
traceCCS f =
  withObDual @k @u $
    obj1 @x ** dualityUnitS @u
      == f ** obj1 @(Dual u)
      == obj1 @y ** (swap2 @u @(Dual u) == dualityCounitS @u)

traceCC :: forall {k} u (x :: k) y. (CompactClosed k, Ob x, Ob y, Ob u) => x ** u ~> y ** u -> x ~> y
traceCC f = unStr (traceCCS @u (Str f))

coactCC
  :: forall {m} {k} (t :: (m, k) +-> k) (u :: m) (x :: k) (y :: k)
   . (CompactClosed m, MonoidalAction t, Ob x, Ob y, Ob u) => Act t u x ~> Act t u y -> x ~> y
coactCC f =
  unitor @t @y
    . actHom @t (dualityCounit @_ @u) (obj @y)
    . multiplicatorInv @t @(Dual u) @u @y
    . actHom @t (obj @(Dual u)) f
    . multiplicator @t @(Dual u) @u @x
    . actHom @t (swap @m @u @(Dual u) . dualityUnit @_ @u) (obj @x)
    . unitorInv @t @x
    \\ dualObj @u

instance CompactClosed () where
  distribDual = U.Unit
  dualUnit = U.Unit
  dualityUnit = U.Unit
  dualityCounit = U.Unit

instance (CompactClosed j, CompactClosed k) => CompactClosed (j, k) where
  distribDual @'(a, a') @'(b, b') = distribDual @j @a @b :**: distribDual @k @a' @b'
  dualUnit = dualUnit :**: dualUnit
  dualityUnit @'(a, a') = dualityUnit @j @a :**: dualityUnit @k @a'
  dualityCounit @'(a, a') = dualityCounit @j @a :**: dualityCounit @k @a'

-- | The structures the free category needs for 'CompactClosed', and those its laws are stated for.
type CompactClosedStructures :: [Kind -> Constraint]
type CompactClosedStructures = '[Monoidal, SymMonoidal, Closed, StarAutonomous, CompactClosed]

instance
  (CompactClosedStructures `Elems` cs)
  => HasStructure cs (p :: CAT k) CompactClosed
  where
  data Struct CompactClosed a b where
    DistribDual :: (Ob a, Ob b) => Struct CompactClosed (DualF (a **! b)) (DualF a **! DualF b)
    DualUnit :: Struct CompactClosed (DualF UnitF) UnitF
  foldStructure @f _ (DistribDual @a @b) =
    withLowerOb @f @a (withLowerOb @f @b (distribDual @_ @(Lower f a) @(Lower f b)))
  foldStructure _ DualUnit = dualUnit
instance P.Show (Struct CompactClosed a b) where
  showsPrec _ DistribDual = P.showString "distribDual"
  showsPrec _ DualUnit = P.showString "dualUnit"

instance
  (CompactClosedStructures `Elems` cs)
  => CompactClosed (FREE cs (p :: CAT k))
  where
  distribDual @a @b = St (DistribDual @a @b) Nil
  dualUnit = St DualUnit Nil
  dualityUnit @a = dualityUnitDefault @a
  dualityCounit @a = dualityCounitDefault @a

-- | 'distribDual' and 'dualUnit' are isomorphisms (so 'Dual' is strong monoidal), and 'dualityUnit'
-- and 'dualityCounit' satisfy the zigzag identities, making @Dual a@ dual to @a@.
instance Laws CompactClosedStructures where
  laws =
    inverses "distribDual" (\ @a @b -> Inverses (distribDual @_ @a @b) (label "combineDual" (combineDual @a @b)))
      P.++ inverses "dualUnit" (Inverses dualUnit (label "dualUnitInv" dualUnitInv))
      P.++ [ Law "dualityUnit definition" \ @a _ -> withObDual @_ @a (dualityUnit @_ @a === dualityUnitDefault @a)
           , Law "dualityCounit definition" \ @a _ -> withObDual @_ @a (dualityCounit @_ @a === dualityCounitDefault @a)
           , Law
               "zigzag (a)"
               \ @a _ ->
                 withObDual @_ @a $
                   ( rightUnitor @_ @a
                       . (obj @a ** dualityCounit @_ @a)
                       . associator @_ @a @(Dual a) @a
                       . (dualityUnit @_ @a ** obj @a)
                       . leftUnitorInv @_ @a
                   )
                     === id
           , Law
               "zigzag (Dual a)"
               \ @a _ ->
                 withObDual @_ @a $
                   ( leftUnitor @_ @(Dual a)
                       . (dualityCounit @_ @a ** obj @(Dual a))
                       . associatorInv @_ @(Dual a) @a @(Dual a)
                       . (obj @(Dual a) ** dualityUnit @_ @a)
                       . rightUnitorInv @_ @(Dual a)
                   )
                     === id
           ]