packages feed

moonlight-category-0.1.0.0: src-laws/Moonlight/Category/Effect/Harness/Limits.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- | Executable checks for limit and colimit universal-property laws.
module Moonlight.Category.Effect.Harness.Limits
  ( productProjection1,
    productProjection2,
    coproductInjection1,
    coproductInjection2,
    pullbackCommutative,
    pushoutCommutative,
    equalizerCommutative,
    coequalizerCommutative,
  )
where

import Moonlight.Category.Effect.Harness.Core (composeC, sourceC, targetC)
import Moonlight.Category.Pure.Category (Category (..))
import Moonlight.Category.Pure.Limits
  ( HasCoequalizers (..),
    HasCoproducts (..),
    HasEqualizers (..),
    HasProducts (..),
    HasPullbacks (..),
    HasPushouts (..),
  )
import Prelude hiding (Functor)

productProjection1 :: forall c. (HasProducts c, Eq (Mor c)) => c -> ProductOb c -> Mor c -> Mor c -> Bool
productProjection1 categoryValue productObject first second =
  rightEquals (composeC @c categoryValue (productProj1 @c categoryValue productObject) (productUniversal @c categoryValue first second)) first

productProjection2 :: forall c. (HasProducts c, Eq (Mor c)) => c -> ProductOb c -> Mor c -> Mor c -> Bool
productProjection2 categoryValue productObject first second =
  rightEquals (composeC @c categoryValue (productProj2 @c categoryValue productObject) (productUniversal @c categoryValue first second)) second

coproductInjection1 :: forall c. (HasCoproducts c, Eq (Mor c)) => c -> CoproductOb c -> Mor c -> Mor c -> Bool
coproductInjection1 categoryValue coproductObject first second =
  rightEquals (composeC @c categoryValue (coproductUniversal @c categoryValue first second) (coproductInj1 @c categoryValue coproductObject)) first

coproductInjection2 :: forall c. (HasCoproducts c, Eq (Mor c)) => c -> CoproductOb c -> Mor c -> Mor c -> Bool
coproductInjection2 categoryValue coproductObject first second =
  rightEquals (composeC @c categoryValue (coproductUniversal @c categoryValue first second) (coproductInj2 @c categoryValue coproductObject)) second

pullbackCommutative :: forall c. (HasPullbacks c, Eq (Mor c), Eq (Ob c)) => c -> Mor c -> Mor c -> Bool
pullbackCommutative categoryValue first second =
  case endpointAgreement (targetC @c categoryValue first) (targetC @c categoryValue second) of
    Nothing -> False
    Just False -> True
    Just True ->
      case pullback @c categoryValue first second of
        Nothing -> False
        Just (_, leftLeg, rightLeg) ->
          rightValuesEqual
            (composeC @c categoryValue first leftLeg)
            (composeC @c categoryValue second rightLeg)

pushoutCommutative :: forall c. (HasPushouts c, Eq (Mor c), Eq (Ob c)) => c -> Mor c -> Mor c -> Bool
pushoutCommutative categoryValue first second =
  case endpointAgreement (sourceC @c categoryValue first) (sourceC @c categoryValue second) of
    Nothing -> False
    Just False -> True
    Just True ->
      case pushout @c categoryValue first second of
        Nothing -> False
        Just (_, leftLeg, rightLeg) ->
          rightValuesEqual
            (composeC @c categoryValue leftLeg first)
            (composeC @c categoryValue rightLeg second)

equalizerCommutative :: forall c. (HasEqualizers c, Eq (Mor c), Eq (Ob c)) => c -> Mor c -> Mor c -> Bool
equalizerCommutative categoryValue first second =
  case parallelMorphisms @c categoryValue first second of
    Nothing -> False
    Just False -> True
    Just True ->
      case equalizer @c categoryValue first second of
        Nothing -> False
        Just (_, equalizerMorphism) ->
          rightValuesEqual
            (composeC @c categoryValue first equalizerMorphism)
            (composeC @c categoryValue second equalizerMorphism)

coequalizerCommutative :: forall c. (HasCoequalizers c, Eq (Mor c), Eq (Ob c)) => c -> Mor c -> Mor c -> Bool
coequalizerCommutative categoryValue first second =
  case parallelMorphisms @c categoryValue first second of
    Nothing -> False
    Just False -> True
    Just True ->
      case coequalizer @c categoryValue first second of
        Nothing -> False
        Just (_, coequalizerMorphism) ->
          rightValuesEqual
            (composeC @c categoryValue coequalizerMorphism first)
            (composeC @c categoryValue coequalizerMorphism second)

rightEquals :: Eq value => Either err value -> value -> Bool
rightEquals eitherValue expected =
  case eitherValue of
    Right value -> value == expected
    Left _ -> False

rightValuesEqual :: Eq value => Either left value -> Either right value -> Bool
rightValuesEqual left right =
  case (left, right) of
    (Right leftValue, Right rightValue) -> leftValue == rightValue
    _ -> False

endpointAgreement :: Eq object => Either left object -> Either right object -> Maybe Bool
endpointAgreement left right =
  case (left, right) of
    (Right leftObject, Right rightObject) -> Just (leftObject == rightObject)
    _ -> Nothing

parallelMorphisms :: forall c. (Category c, Eq (Ob c)) => c -> Mor c -> Mor c -> Maybe Bool
parallelMorphisms categoryValue first second = do
  sourcesAgree <- endpointAgreement (sourceC @c categoryValue first) (sourceC @c categoryValue second)
  targetsAgree <- endpointAgreement (targetC @c categoryValue first) (targetC @c categoryValue second)
  pure (sourcesAgree && targetsAgree)