packages feed

moonlight-category-0.1.0.0: src-abstract/Moonlight/Category/Pure/Thin.hs

-- | Thin morphisms over a relation: at most one morphism between any two objects,
-- with relation-checked construction, identity and composition.
module Moonlight.Category.Pure.Thin
  ( ThinMorphism,
    thinMorphismSource,
    thinMorphismTarget,
    mkThinMorphismBy,
    identityThinMorphism,
    composeThinMorphismBy,
  )
where

import Data.Kind (Type)

type ThinMorphism :: Type -> Type
-- | The unique candidate morphism between two objects of a thin category.
data ThinMorphism obj = ThinMorphism
  { thinMorphismSource :: obj,
    thinMorphismTarget :: obj
  }
  deriving stock (Eq, Ord, Show)

-- | Admit a morphism exactly when the supplied relation holds.
mkThinMorphismBy :: (obj -> obj -> Bool) -> obj -> obj -> Maybe (ThinMorphism obj)
mkThinMorphismBy relation sourceValue targetValue =
  if relation sourceValue targetValue
    then Just (ThinMorphism sourceValue targetValue)
    else Nothing

-- | Construct the reflexive identity witness.
identityThinMorphism :: obj -> ThinMorphism obj
identityThinMorphism objectValue =
  ThinMorphism objectValue objectValue

-- | Compose relation-valid morphisms with matching middle endpoints.
composeThinMorphismBy :: Eq obj => (obj -> obj -> Bool) -> ThinMorphism obj -> ThinMorphism obj -> Maybe (ThinMorphism obj)
composeThinMorphismBy relation leftMorphism rightMorphism
  | not (relation (thinMorphismSource leftMorphism) (thinMorphismTarget leftMorphism)) = Nothing
  | not (relation (thinMorphismSource rightMorphism) (thinMorphismTarget rightMorphism)) = Nothing
  | thinMorphismTarget rightMorphism == thinMorphismSource leftMorphism =
      mkThinMorphismBy
        relation
        (thinMorphismSource rightMorphism)
        (thinMorphismTarget leftMorphism)
  | otherwise = Nothing