proarrow-0.1.0.0: test/Props/FinSet.hs
{-# LANGUAGE OverloadedLists #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Props.FinSet where
import Data.Fin (Fin, absurd, universe)
import Data.Proxy (Proxy (..))
import Data.Type.Equality (TestEquality (..), type (:~:) (..))
import Data.Type.Nat (Nat0, Nat1, Nat2, Nat3, Nat4, SNat (..), SNatI, reflect, snat)
import Data.Vec.Lazy (Vec (..), repeat)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Falsify (testProperty)
import Prelude qualified as P
import Proarrow.Category.Instance.FinSet (FINSET (..), FinSet (..))
import Proarrow.Category.Topos (closedTopology, doubleNegation, openTopology)
import Proarrow.Core (CategoryOf (..), Promonad (..), UN)
import Proarrow.Testing
( GenTotal (..)
, Testable (..)
, TestableProfunctor
, TestableType (..)
, TestingEqShow (..)
, genSomeDef
, invmap
, oneElem
, optGen
, testEq
, pattern GenNonEmpty
)
import Proarrow.Testing.Laws
test :: TestTree
test =
testGroup
"FinSet"
[ testCategory @FINSET
, testTerminalObject @FINSET
, testInitialObject @FINSET
, testBinaryProducts_ @FINSET
, testCartesian_ @FINSET
, testMonoidal_ @FINSET
, testSymMonoidal_ @FINSET
, testCopyDiscard_ @FINSET
, testBinaryCoproducts_ @FINSET
, testDistributive_ @FINSET
, testClosed_ @FINSET
, testEqualizers_ @FINSET
, testCoequalizers_ @FINSET
, testEpiMonoFactorization_ @FINSET
, testSubobjectClassifier_ @FINSET
, -- FINSET is Boolean, so its only topologies are the two extremes, and ¬¬ is the identity
testGroup
"Lawvere-Tierney topologies"
[ testLawvereTierney_ @FINSET doubleNegation
, testLawvereTierneyFamily_ @FINSET "open" openTopology
, testLawvereTierneyFamily_ @FINSET "closed" closedTopology
, testProperty "double negation is the identity" P.$
testEq "¬¬" "doubleNegation" (doubleNegation @FINSET) "id" id
, testNegation @FINSET
]
, testPullbacks_ @FINSET
, testPushouts_ @FINSET
, testComonoid_ @(FS Nat0)
, testComonoid_ @(FS Nat1)
, testComonoid_ @(FS Nat2)
, testComonoid_ @(FS Nat3)
, testMonoid_ @(FS Nat1)
, -- The product category, whose tensor, products and coproducts all go through the
-- projections, so that 'Cartesian' can see the tensor as the product at all. We use
-- FINSET, not a thin category: there parallel arrows are equal, so these laws could only
-- check that the arrows evaluate. Both factors are the same, so mixing up the components
-- still type-checks and has to be caught here.
testGroup
"FINSET x FINSET"
[ testCategory @(FINSET, FINSET)
, testTerminalObject @(FINSET, FINSET)
, testInitialObject @(FINSET, FINSET)
, testBinaryProducts_ @(FINSET, FINSET)
, testBinaryCoproducts_ @(FINSET, FINSET)
, testMonoidal_ @(FINSET, FINSET)
, testSymMonoidal_ @(FINSET, FINSET)
, testCopyDiscard_ @(FINSET, FINSET)
, testCartesian_ @(FINSET, FINSET)
, testDistributive_ @(FINSET, FINSET)
]
]
-- | Two finite sets are the same object when they have the same cardinality. Not a method of
-- 'Testable': no law needs to compare objects (see "Props.Span"\'s 'eqP' for why the ones that do
-- are comparing something existential).
eqFinSet :: forall (a :: FINSET) (b :: FINSET). (Ob a, Ob b) => P.Maybe (a :~: b)
eqFinSet = (\Refl -> Refl) P.<$> testEquality (snat @(UN FS a)) (snat @(UN FS b))
instance Testable FINSET where
type TestOb a = Ob a
showOb @(FS a) = P.show (reflect (Proxy @a))
genSome = genSomeDef @'[FS Nat0, FS Nat1, FS Nat2, FS Nat3, FS Nat4]
instance (Ob a, Ob b) => TestingEqShow (FinSet a b)
instance (Ob a, Ob b) => TestableType (FinSet a b) where
gen = invmap FinSet unFinSet gen
instance TestableProfunctor FinSet
instance (P.Eq a, P.Show a) => TestingEqShow (Vec n a)
instance (P.Eq a, P.Show a, TestableType a, SNatI n) => TestableType (Vec n a) where
gen = case gen of
GenEmpty absrd -> case snat @n of
SZ -> oneElem VNil
SS -> GenEmpty \(a ::: _) -> absrd a
GenNonEmpty g -> GenNonEmpty (P.sequence (repeat @n g))
instance (SNatI n) => TestingEqShow (Fin n)
instance (SNatI n) => TestableType (Fin n) where
gen = case snat @n of
SZ -> GenEmpty absurd
SS -> optGen universe