proarrow-0.1.0.0: test/Props/Discrete.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Props.Discrete where
import Data.Type.Equality (type (:~:))
import Data.Type.Equality qualified as Eq
import Data.Type.Nat (SNat (..), snat)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Falsify (testProperty)
import Prelude
import Proarrow.Category.Enriched.Thin
( DecidableProfunctor (..)
, Decision (..)
, Holds
, Indexed (..)
, KnownIndex
, Objects
, ThinProfunctor (..)
)
import Proarrow.Category.Enriched.Thin.Composition (Closure)
import Proarrow.Category.Instance.Bool (BOOL (..), Booleans (..))
import Proarrow.Category.Instance.Discrete (CODISCRETE (..), Codiscrete, DISCRETE (..), Discrete (..))
import Proarrow.Core (CAT, Profunctor (..), UN)
test :: TestTree
test =
testGroup
"Discrete"
[ testProperty "reachability over a bare set finds the edge" $ withArr reachEdge (pure ())
, testProperty "distinct points are decided apart" $ case decide @Discrete @(D FLS) @(D TRU) of
No -> pure ()
]
-- | A point of the bare set @DISCRETE BOOL@ is recovered from its index alone.
pointOf :: forall (a :: DISCRETE BOOL). (KnownIndex a) => Booleans (UN D a) (UN D a)
pointOf = case snat @(Index a) of
SZ -> Fls
SS @i -> case snat @i of SZ -> Tru
-- | The graph with the single edge @FLS -> TRU@ on the bare two-point set: unlike over the walking
-- arrow, there is no base arrow to fall back on.
type Edge :: CAT (DISCRETE BOOL)
data Edge a b where
FT :: Edge (D FLS) (D TRU)
instance Profunctor Edge where
dimap Refl Refl e = e
r \\ FT = r
type family EdgeHolds (a :: BOOL) (b :: BOOL) :: BOOL where
EdgeHolds FLS TRU = TRU
EdgeHolds a b = FLS
instance ThinProfunctor Edge
instance DecidableProfunctor Edge where
type Holds Edge a b = EdgeHolds (UN D a) (UN D b)
decide @a @b = case (pointOf @a, pointOf @b) of
(Fls, Fls) -> No
(Fls, Tru) -> Yes FT
(Tru, Fls) -> No
(Tru, Tru) -> No
toHolds FT r = r
-- | The closure over the bare set: the edge is found, its reverse is not, and points reach themselves.
reachEdge :: Closure Edge (D FLS) (D TRU)
reachEdge = arr
noWayBack :: Holds (Closure Edge) (D TRU) (D FLS) :~: FLS
noWayBack = Eq.Refl
reachSelf :: Holds (Closure Edge) (D TRU) (D TRU) :~: TRU
reachSelf = Eq.Refl
-- | The discrete category itself is decided by comparing indices.
samePoint :: Holds (Discrete :: CAT (DISCRETE BOOL)) (D FLS) (D FLS) :~: TRU
samePoint = Eq.Refl
otherPoint :: Holds (Discrete :: CAT (DISCRETE BOOL)) (D FLS) (D TRU) :~: FLS
otherPoint = Eq.Refl
-- * The codiscrete category on the same points
-- | Its objects are the points of @k@, in the same order.
objectsCodiscrete :: Objects (CODISCRETE BOOL) :~: '[CD FLS, CD TRU]
objectsCodiscrete = Eq.Refl
-- | Every point reaches every other, and the closure computes that by searching the points. This
-- only typechecks because the codiscrete category is enumerable.
codiscreteReaches :: Holds (Closure (Codiscrete :: CAT (CODISCRETE BOOL))) (CD TRU) (CD FLS) :~: TRU
codiscreteReaches = Eq.Refl