{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE UndecidableInstances #-} #if __GLASGOW_HASKELL__ >= 806 {-# LANGUAGE QuantifiedConstraints #-} #endif {-# OPTIONS_GHC -Wno-orphans #-} module Test.Cat (tests) where import Prelude hiding ((.), id) import Control.Category import Data.Function (on) #if __GLASGOW_HASKELL__ < 804 import Data.Monoid (Monoid (..)) import Data.Semigroup (Semigroup (..)) #endif import Text.Show.Functions () import Numeric.Natural (Natural) import Control.Algebra.Free2 import Control.Category.Free import Test.QuickCheck import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) tests :: TestTree tests = testGroup "Control.Category.Free" [ testProperty "Queue" prop_Queue , testProperty "C" prop_C , testGroup "Category laws" [ testProperty "ListTr id" prop_id_ListTr , testProperty "ListTr associativity" prop_associativity_ListTr , testProperty "Queue id" prop_id_Queue , testProperty "Queue associativity" prop_associativity_Queue , testProperty "C id" prop_id_C , testProperty "C associativity" prop_associativity_C ] , testGroup "foldFree2 and foldMap" [ testProperty "foldFree ListTr" prop_foldListTr , testProperty "foldFree Queue" prop_foldQueue , testProperty "foldFree C" prop_foldC ] ] data Tr a b where -- Num transition NumTr :: Num a => (a -> a) -> Tr a a FromInteger :: Num b => Tr Integer b -- Integral transition ToInteger :: Integral a => Tr a Integer interpretTr :: Tr a b -> a -> b interpretTr (NumTr f) = f interpretTr FromInteger = fromInteger interpretTr ToInteger = toInteger instance (Show a, Show b) => Show (Tr a b) where show (NumTr f) = "NumTr " ++ show f show FromInteger = "FromInteger" show ToInteger = "ToInteger" data SomeNumTr f a where SomeNumTr :: Num a => f Tr a a -> SomeNumTr f a instance Show (f Tr a a) => Show (SomeNumTr f a) where show (SomeNumTr f) = "SomeNumTr " ++ show f data SomeIntegralTr f a where SomeIntegralTr :: Integral a => f Tr a a -> SomeIntegralTr f a instance Show (f Tr a a) => Show (SomeIntegralTr f a) where show (SomeIntegralTr f) = "SomeIntegralTr " ++ show f -- A 'fromIntegral' transition in any free category @f@. fromIntegralTr :: ( Integral a , Num b , Category (f Tr) , AlgebraType0 f Tr , FreeAlgebra2 f ) => f Tr a b fromIntegralTr = liftFree2 FromInteger . liftFree2 ToInteger data Sing a where SInt :: Sing Int SInteger :: Sing Integer SNatural :: Sing Natural instance Show (Sing a) where show SInt = "SInt" show SInteger = "SInteger" show SNatural = "SNatural" data AnySing where AnySing :: Eq a => Sing a -> AnySing instance Eq AnySing where AnySing SInt == AnySing SInt = True AnySing SInteger == AnySing SInteger = True AnySing SNatural == AnySing SNatural = True _ == _ = False instance Show AnySing where show (AnySing sing) = show sing instance Arbitrary AnySing where arbitrary = oneof [ pure $ AnySing SInt , pure $ AnySing SInteger , pure $ AnySing SNatural ] instance Arbitrary Natural where arbitrary = fromIntegral . getPositive <$> (arbitrary :: Gen (Positive Integer)) instance CoArbitrary Natural where coarbitrary a = variant (fromIntegral a :: Int) data AnyListTr b where AnyListTr :: Eq c => ListTr Tr b c -> Sing c -> AnyListTr b genNextTr :: Sing b -> Gen (AnyListTr b) genNextTr b = do AnySing c <- arbitrary case (b, c) of (SInt, SInt) -> (\f -> AnyListTr (ConsTr (NumTr f) NilTr) c) <$> arbitrary (SInteger, SInteger) -> (\f -> AnyListTr (ConsTr (NumTr f) NilTr) c) <$> arbitrary (SNatural, SNatural) -> (\f -> AnyListTr (ConsTr (NumTr f) NilTr) c) <$> arbitrary (SInt, SInteger) -> pure $ AnyListTr fromIntegralTr c (SInt, SNatural) -> pure $ AnyListTr (fromIntegralTr . liftFree2 (NumTr abs)) c (SInteger, SInt) -> pure $ AnyListTr fromIntegralTr c (SNatural, SInt) -> pure $ AnyListTr fromIntegralTr c (SNatural, SInteger) -> pure $ AnyListTr fromIntegralTr c (SInteger, SNatural) -> pure $ AnyListTr (fromIntegralTr . liftFree2 (NumTr abs)) c data ArbListTr where ArbListTr :: Eq b => ListTr Tr a b -> Sing a -> Sing b -> ArbListTr #if __GLASGOW_HASKELL__ >= 806 instance (forall x y. Show (Tr x y)) => Show ArbListTr where show (ArbListTr listTr a b) = "ArbListTr " ++ show a ++ " -> " ++ show b ++ " " ++ show listTr #else instance Show ArbListTr where show (ArbListTr _listTr a b) = "ArbListTr " ++ show a ++ " -> " ++ show b #endif instance Arbitrary ArbListTr where arbitrary = sized $ \n -> do k <- choose (0, n) AnySing a <- arbitrary go k a (AnyListTr NilTr a) where go 0 a (AnyListTr ab b) = pure $ ArbListTr ab a b go n a (AnyListTr ab b) = do AnyListTr bc c <- genNextTr b -- (.) can be used as (++) for ListTr go (n - 1) a $ AnyListTr (bc . ab) c -- -- test 'Cat' and 'C' treating 'ListTr' as a model to compare to. -- prop_Queue, prop_C :: Blind ArbListTr -> Bool prop_Queue (Blind (ArbListTr listTr SInt _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @Queue listTr) 0 == foldNatFree2 interpretTr listTr 0 prop_Queue (Blind (ArbListTr listTr SInteger _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @Queue listTr) 0 == foldNatFree2 interpretTr listTr 0 prop_Queue (Blind (ArbListTr listTr SNatural _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @Queue listTr) 0 == foldNatFree2 interpretTr listTr 0 prop_C (Blind (ArbListTr listTr SInt _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @C listTr) 0 == foldNatFree2 interpretTr listTr 0 prop_C (Blind (ArbListTr listTr SInteger _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @C listTr) 0 == foldNatFree2 interpretTr listTr 0 prop_C (Blind (ArbListTr listTr SNatural _)) = foldNatFree2 interpretTr (hoistFreeH2 @_ @C listTr) 0 == foldNatFree2 interpretTr listTr 0 -- -- Test Category Laws -- @ -- f . id == f == id . f -- f . g . h == (f . g) . h -- @ -- prop_id :: Category c => (c a b -> c a b -> Bool) -> c a b -> Bool prop_id eqCat f = eqCat (f . id) f && eqCat (id . f) f prop_associativity :: Category c => (c x w -> c x w -> Bool) -> c z w -> c y z -> c x y -> Bool prop_associativity eqCat f g h = (f . g . h) `eqCat` ((f . g) . h) -- | Integers form commutative monoid, and thus a category (a groupoid to be -- precise) with a single object. -- data IntCat (a :: ()) (b :: ()) where IntCat :: Int -> IntCat a a instance Show (IntCat a b) where show (IntCat i) = "IntCat " ++ show i instance Eq (IntCat a b) where IntCat i == IntCat j = i == j instance Category IntCat where id = IntCat 0 IntCat a . IntCat b = IntCat (a + b) instance Semigroup (IntCat '() '()) where IntCat a <> IntCat b = IntCat (a + b) instance Monoid (IntCat '() '()) where mempty = IntCat 0 #if __GLASGOW_HASKELL__ < 804 mappend = (<>) #endif instance Arbitrary (IntCat '() '()) where arbitrary = IntCat <$> arbitrary fromList :: forall k (a :: k) m f. ( FreeAlgebra2 m , AlgebraType0 m f , Category (m f) ) => [f a a] -> m f a a fromList [] = id fromList (f : fs) = liftFree2 f . fromList fs toList :: ( FreeAlgebra2 m , AlgebraType0 m IntCat , AlgebraType m (ListTr IntCat) ) => m IntCat '() '() -> [IntCat '() '()] toList c = go (hoistFreeH2 c) where go :: ListTr IntCat '() '() -> [IntCat '() '()] go NilTr = [] go (ConsTr tr@IntCat{} xs) = tr : go xs -- -- 'C' category laws -- newtype ArbIntC = ArbIntC (C IntCat '() '()) instance Show ArbIntC where show (ArbIntC c) = show c instance Arbitrary ArbIntC where arbitrary = ArbIntC . fromList <$> arbitrary shrink (ArbIntC c) = map (ArbIntC . fromList) $ shrinkList (const []) $ toList c prop_id_C :: ArbIntC -> Bool prop_id_C (ArbIntC f) = prop_id (on (==) toList) f prop_associativity_C :: ArbIntC -> ArbIntC -> ArbIntC -> Bool prop_associativity_C (ArbIntC f0) (ArbIntC f1) (ArbIntC f2) = prop_associativity (on (==) toList) f0 f1 f2 -- -- 'Queue' category laws -- newtype ArbIntQueue = ArbIntQueue (Queue IntCat '() '()) instance Show ArbIntQueue where show (ArbIntQueue f) = show (toList f) instance Arbitrary ArbIntQueue where arbitrary = ArbIntQueue . fromList <$> arbitrary shrink (ArbIntQueue c) = map (ArbIntQueue . fromList) $ shrinkList (const []) $ toList c prop_id_Queue :: ArbIntQueue -> Bool prop_id_Queue (ArbIntQueue f) = prop_id (on (==) toList) f prop_associativity_Queue :: ArbIntQueue -> ArbIntQueue -> ArbIntQueue -> Bool prop_associativity_Queue (ArbIntQueue f0) (ArbIntQueue f1) (ArbIntQueue f2) = prop_associativity (on (==) toList) f0 f1 f2 -- -- 'ListTr' category laws -- newtype ArbIntListTr = ArbIntListTr (ListTr IntCat '() '()) instance Show ArbIntListTr where show (ArbIntListTr f) = show (toList f) instance Arbitrary ArbIntListTr where arbitrary = ArbIntListTr . fromList <$> arbitrary shrink (ArbIntListTr c) = map (ArbIntListTr . fromList) $ shrinkList (const []) $ toList c prop_id_ListTr :: ArbIntListTr -> Bool prop_id_ListTr (ArbIntListTr f) = prop_id (on (==) toList) f prop_associativity_ListTr :: ArbIntListTr -> ArbIntListTr -> ArbIntListTr -> Bool prop_associativity_ListTr (ArbIntListTr f0) (ArbIntListTr f1) (ArbIntListTr f2) = prop_associativity (on (==) toList) f0 f1 f2 -- -- Compatibility between 'foldFree2' and 'foldMap' for 'IntCat' -- prop_foldListTr :: ArbIntListTr -> Bool prop_foldListTr (ArbIntListTr f) = foldFree2 f == foldMap id (toList f) prop_foldQueue :: ArbIntQueue -> Bool prop_foldQueue (ArbIntQueue f) = foldFree2 f == foldMap id (toList f) prop_foldC :: (Blind ArbIntC) -> Bool prop_foldC (Blind (ArbIntC f)) = foldFree2 f == foldMap id (toList f)