packages feed

proarrow-0.1.0.0: test/Props/Ordinal.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | The finite ordinals as an enumerable thin category: composing the order with itself is
-- transitivity, computed by searching the objects.
module Props.Ordinal where

import Data.Type.Equality ((:~:) (Refl))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Falsify (testProperty)
import Prelude

import Proarrow.Category.Enriched.Finitary (objIndex)
import Proarrow.Category.Enriched.Thin (Holds, Objects, ThinProfunctor (..))
import Proarrow.Category.Enriched.Thin.Composition ()
import Proarrow.Category.Instance.Bool (BOOL (..))
import Proarrow.Category.Instance.Ordinal (LTE, ORDINAL (..), ORDINAL3)
import Proarrow.Core (CAT, Ob)
import Proarrow.Profunctor.Instance.Composition ((:.:))
import Proarrow.Testing
  ( Testable (..)
  , TestableProfunctor
  , TestableType (..)
  , TestingEqShow (..)
  , genElements
  , genSomeFinite
  )
import Proarrow.Testing.Laws
  ( testBinaryCoproducts_
  , testBinaryProducts_
  , testCartesian_
  , testCategory
  , testCopyDiscard_
  , testDistributive_
  , testInitialObject
  , testMonoidal_
  , testSymMonoidal_
  , testTerminalObject
  )

test :: TestTree
test =
  testGroup
    "Ordinal"
    [ testProperty "composing the order searches the objects" $ withArr transitive (pure ())
    , -- the chain as a distributive lattice: meet the minimum and tensor, join the maximum
      testCategory @ORDINAL3
    , testTerminalObject @ORDINAL3
    , testInitialObject @ORDINAL3
    , testBinaryProducts_ @ORDINAL3
    , testBinaryCoproducts_ @ORDINAL3
    , testMonoidal_ @ORDINAL3
    , testSymMonoidal_ @ORDINAL3
    , testCopyDiscard_ @ORDINAL3
    , testCartesian_ @ORDINAL3
    , testDistributive_ @ORDINAL3
    ]

instance Testable ORDINAL3 where
  showOb @a = show (objIndex @a)
  genSome = genSomeFinite

instance (Ob a, Ob b) => TestableType (LTE (a :: ORDINAL3) b) where
  gen = genElements @LTE

-- | Thin, so parallel arrows are equal for free. Forcing is the one thing left to check.
instance (Ob a, Ob b) => TestingEqShow (LTE (a :: ORDINAL3) b) where
  eqP l r = l `seq` r `seq` pure True
  showP _ = show (objIndex @a) ++ "<=" ++ show (objIndex @b)

instance TestableProfunctor (LTE :: CAT ORDINAL3)

-- | The three ordinals, in order.
objectsOrdinal3 :: Objects ORDINAL3 :~: '[OZ, OS OZ, OS (OS OZ)]
objectsOrdinal3 = Refl

-- | Neither leg is representable, so the composite is decided by searching the middle ordinal:
-- @0 <= 2@ holds because it factors through @1@ (among others).
transitive :: ((LTE :.: LTE) :: CAT ORDINAL3) OZ (OS (OS OZ))
transitive = arr

-- | And there is no way back down.
notTransitive :: Holds ((LTE :.: LTE) :: CAT ORDINAL3) (OS (OS OZ)) OZ :~: FLS
notTransitive = Refl