packages feed

proarrow-0.1.0.0: src/Proarrow/Category/Instance/Discrete.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- | The __discrete__ category on an 'Thin.Indexed' kind @k@ (@'DISCRETE' k@): the numbered inhabitants
-- of @k@ are the objects and the only arrows are identities ('Refl'). The numbering makes the
-- category decidable, and a 'Thin.Finite' kind gives an enumerable one, so that reachability along
-- a graph on a bare set of points can be computed. Its mirror image, the __codiscrete__ category
-- @CODISCRETE k@, has exactly one arrow between any two objects. All (co)limits that exist are
-- trivially computed.
module Proarrow.Category.Instance.Discrete where

import Data.Type.Equality (type (~~))
import Data.Type.Equality qualified as Eq
import Data.Type.Nat (snat)
import Prelude (type (~))

import Proarrow.Category.Enriched (EnrichedProfunctor (..))
import Proarrow.Category.Enriched.Dagger (DaggerProfunctor (..))
import Proarrow.Category.Enriched.Quantale (Quantale (..), bottomTensor)
import Proarrow.Category.Enriched.Thin qualified as Thin
import Proarrow.Category.Instance.Bool (BOOL (..), If)
import Proarrow.Category.Instance.Cost (COST)
import Proarrow.Category.Monoidal (Monoidal (..))
import Proarrow.Category.Topos (HasEpiMonoFactorization (..))
import Proarrow.Colimit.BinaryCoproduct (HasBinaryCoproducts (..))
import Proarrow.Colimit.Coequalizer (HasCoequalizers (..), thinCoequalize)
import Proarrow.Colimit.Initial (HasInitialObject (..))
import Proarrow.Colimit.Pushout (HasPushouts (..))
import Proarrow.Core (CAT, CategoryOf (..), Kind, Profunctor (..), Promonad (..), UN, dimapDefault, obj)
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Equalizer (HasEqualizers (..), thinEqualize)
import Proarrow.Limit.Pullback (HasPullbacks (..))

type data DISCRETE k = D k

type Discrete :: CAT (DISCRETE k)
data Discrete a b where
  Refl :: (Ob a) => Discrete a a

-- | The discrete category with only identity arrows on the numbered inhabitants of @k@.
instance (Thin.Indexed k) => CategoryOf (DISCRETE k) where
  type (~>) = Discrete
  type Ob (a :: DISCRETE k) = Thin.KnownIndex a

instance (Thin.Indexed k) => Profunctor (Discrete :: CAT (DISCRETE k)) where
  dimap = dimapDefault
  r \\ Refl = r
instance (Thin.Indexed k) => Promonad (Discrete :: CAT (DISCRETE k)) where
  id = Refl
  Refl . Refl = Refl

instance (Thin.Indexed k) => Thin.ThinProfunctor (Discrete :: CAT (DISCRETE k)) where
  type HasArrow Discrete a b = (a ~~ b)
  arr = Refl
  withArr Refl r = r

-- | An arrow of @'DISCRETE' k@ is an equality. This also witnesses that the category is discrete:
-- it only typechecks because 'Thin.withEq' demands it.
withEq :: forall {k} (a :: DISCRETE k) b r. (Thin.Indexed k) => Discrete a b -> ((a ~~ b) => r) -> r
withEq p r = Thin.withEq p r

-- | Two points are equal exactly when their indices are.
instance (Thin.Indexed k) => Thin.DecidableProfunctor (Discrete :: CAT (DISCRETE k)) where
  type Holds (Discrete :: CAT (DISCRETE k)) a b = Thin.Equal a b
  decide @a @b = Thin.mapDecision (\Eq.Refl -> Refl) (Thin.decideEq @a @b)
  toHolds @a Refl r = Thin.withNatEqRefl (snat @(Thin.Index a)) r

-- | The hom-object of the discrete category in a quantale: the unit on the diagonal, the bottom off
-- it. Points are at distance @0@ from themselves and infinitely far from each other: the discrete
-- category is a (discrete) Lawvere metric space, the base for shortest paths on a bare set of points.
type Delta :: forall (v :: Kind) -> BOOL -> v
type Delta v c = If c (Unit :: v) InitialObject

-- | The action of the discrete base on a matrix over the points: on the diagonal the 'Delta' is the
-- unit and the action is the unitor, off it the 'Delta' is the bottom and the action absorbs. The
-- argument says how the matrix is reindexed on the diagonal.
deltaAct
  :: forall {k} {v} (x :: k) y (w :: v) w'
   . (Quantale v, Thin.KnownIndex x, Thin.KnownIndex y, Ob w, Ob w')
  => ((x ~ y) => w Eq.:~: w') -> (Delta v (Thin.Equal x y) ** w) ~> w'
deltaAct eq = case Thin.decideEq @x @y of
  Thin.Yes Eq.Refl -> case eq of Eq.Refl -> leftUnitor @v @w
  Thin.No -> bottomTensor @w @w'

instance (Thin.Indexed k) => EnrichedProfunctor COST (Discrete :: CAT (DISCRETE k)) where
  type ProObj COST (Discrete :: CAT (DISCRETE k)) a b = Delta COST (Thin.Equal a b)
  withProObj @a @b r = case Thin.decideEq @a @b of
    Thin.Yes Eq.Refl -> r
    Thin.No -> r
  underlying @a Refl = Thin.withNatEqRefl (snat @(Thin.Index a)) (obj @(Unit :: COST))
  enriched @a @b f = case Thin.decideEq @a @b of
    Thin.Yes Eq.Refl -> Refl
    Thin.No -> unitIsNotBottom @COST f
  rmap @a @b @c =
    withProObj @COST @(Discrete :: CAT (DISCRETE k)) @a @b
      ( withProObj @COST @(Discrete :: CAT (DISCRETE k)) @a @c
          (deltaAct @b @c @(Delta COST (Thin.Equal a b)) @(Delta COST (Thin.Equal a c)) Eq.Refl)
      )
  lmap @a @b @c =
    withProObj @COST @(Discrete :: CAT (DISCRETE k)) @a @b
      ( withProObj @COST @(Discrete :: CAT (DISCRETE k)) @c @b
          (deltaAct @c @a @(Delta COST (Thin.Equal a b)) @(Delta COST (Thin.Equal c b)) Eq.Refl)
      )

instance (Thin.Indexed k) => Thin.Indexed (DISCRETE k) where
  type Index (a :: DISCRETE k) = Thin.Index (UN D a)
  type At (DISCRETE k) i = Thin.FmapWrap D (Thin.At k i)

instance (Thin.Finite k) => Thin.Finite (DISCRETE k) where
  type Objects (DISCRETE k) = Thin.MapWrap D (Thin.Objects k)
  finite = Thin.wrapFinite @D
  withAtLookup = Thin.withWrapAtLookup @D

instance (Thin.Finite k) => Thin.Enumerable (DISCRETE k) where
  withIndex r = r
  withOb r = r

instance (Thin.Indexed k) => DaggerProfunctor (Discrete :: CAT (DISCRETE k)) where
  dagger Refl = Refl

instance (Thin.Indexed k) => HasEqualizers (DISCRETE k) where
  equalize = thinEqualize
  factorEqualizer Refl Refl = Refl

instance (Thin.Indexed k) => HasCoequalizers (DISCRETE k) where
  coequalize = thinCoequalize
  factorCoequalizer Refl Refl = Refl

instance (Thin.Indexed k) => HasPullbacks (DISCRETE k) where
  pullback Refl Refl k = k Refl Refl
  factorPullback Refl Refl Refl Refl = Refl

instance (Thin.Indexed k) => HasPushouts (DISCRETE k) where
  pushout Refl Refl k = k Refl Refl
  factorPushout Refl Refl Refl Refl = Refl

instance (Thin.Indexed k) => HasEpiMonoFactorization (DISCRETE k)

type data CODISCRETE k = CD k

type Codiscrete :: CAT (CODISCRETE k)
data Codiscrete a b where
  Arr :: (Ob a, Ob b) => Codiscrete a b

-- | The codiscrete category has exactly one arrow between any two objects, the numbered inhabitants
-- of @k@. The numbering makes it enumerable, so its closure can be computed.
instance (Thin.Indexed k) => CategoryOf (CODISCRETE k) where
  type (~>) = Codiscrete
  type Ob (a :: CODISCRETE k) = Thin.KnownIndex a

instance (Thin.Indexed k) => Profunctor (Codiscrete :: CAT (CODISCRETE k)) where
  dimap = dimapDefault
  r \\ Arr = r
instance (Thin.Indexed k) => Promonad (Codiscrete :: CAT (CODISCRETE k)) where
  id = Arr
  Arr . Arr = Arr

instance (Thin.Indexed k) => Thin.ThinProfunctor (Codiscrete :: CAT (CODISCRETE k))

instance (Thin.Indexed k) => Thin.DecidableProfunctor (Codiscrete :: CAT (CODISCRETE k)) where
  type Holds Codiscrete a b = TRU
  decide = Thin.Yes Arr
  toHolds Arr r = r

-- | Witnesses that @'CODISCRETE' k@ really is codiscrete: this only typechecks if 'Codiscrete' is a
-- 'Thin.CodiscreteProfunctor', so the definition is the check.
anyArr :: forall {k} (a :: CODISCRETE k) b. (Thin.Indexed k, Ob a, Ob b) => Codiscrete a b
anyArr = Thin.anyArr

instance (Thin.Indexed k) => Thin.Indexed (CODISCRETE k) where
  type Index (a :: CODISCRETE k) = Thin.Index (UN CD a)
  type At (CODISCRETE k) i = Thin.FmapWrap CD (Thin.At k i)

instance (Thin.Finite k) => Thin.Finite (CODISCRETE k) where
  type Objects (CODISCRETE k) = Thin.MapWrap CD (Thin.Objects k)
  finite = Thin.wrapFinite @CD
  withAtLookup = Thin.withWrapAtLookup @CD

instance (Thin.Finite k) => Thin.Enumerable (CODISCRETE k) where
  withIndex r = r
  withOb r = r

instance (Thin.Indexed k) => DaggerProfunctor (Codiscrete :: CAT (CODISCRETE k)) where
  dagger Arr = Arr

instance (Thin.Indexed k) => HasEqualizers (CODISCRETE k) where
  equalize = thinEqualize
  factorEqualizer Arr Arr = Arr

instance (Thin.Indexed k) => HasCoequalizers (CODISCRETE k) where
  coequalize = thinCoequalize
  factorCoequalizer Arr Arr = Arr

instance (Thin.Indexed k) => HasPullbacks (CODISCRETE k) where
  pullback @o Arr Arr k = k @o Arr Arr
  factorPullback Arr Arr Arr Arr = Arr

instance (Thin.Indexed k) => HasPushouts (CODISCRETE k) where
  pushout @o Arr Arr k = k @o Arr Arr
  factorPushout Arr Arr Arr Arr = Arr

instance (Thin.Indexed k) => HasEpiMonoFactorization (CODISCRETE k)

-- | Any object works as the product of any two objects here, since every hom-set is a singleton.
instance (Thin.Indexed k) => HasBinaryProducts (CODISCRETE k) where
  type a && b = a
  withObProd r = r
  fst = Arr
  snd = Arr
  Arr &&& Arr = Arr

-- | Dual to the 'HasBinaryProducts' instance above.
instance (Thin.Indexed k) => HasBinaryCoproducts (CODISCRETE k) where
  type a || b = a
  withObCoprod r = r
  lft = Arr
  rgt = Arr
  Arr ||| Arr = Arr