{-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main (main) where import Control.Monad.Constrained import qualified Prelude import Test.DocTest import Test.QuickCheck import Data.Proxy import qualified Control.Applicative import Data.Set (Set) import Control.Monad.Constrained.IntSet (IntSet) import GHC.Exts (fromList) import Data.Functor.Classes import Control.Monad.Trans.Reader (ReaderT(..), Reader) import Control.Monad.Trans.State.Strict (StateT(..), State) import Data.Functor.Identity instance Functor Gen where type Suitable Gen a = () fmap = Prelude.fmap (<$) = (Prelude.<$) instance Applicative Gen where lower = lowerP instance Monad Gen where (>>=) = (Prelude.>>=) instance a ~ Int => Arbitrary (IntSet a) where arbitrary = fmap fromList arbitrary fmapIsSame :: (Functor f, Prelude.Functor f, Suitable f b, Eq (f b), Show (f b)) => Blind (a -> b) -> f a -> Property fmapIsSame (Blind f) x = label "fmap is same" $ fmap f x === Prelude.fmap f x replaceIsSame :: (Functor f, Prelude.Functor f, Suitable f a, Eq (f a), Show (f a)) => f b -> a -> Property replaceIsSame xs x = label "replace is same" $ (x <$ xs) === (x Prelude.<$ xs) pureIsSame :: (Applicative f ,Prelude.Applicative f ,Suitable f a ,Eq (f a) ,Show (f a)) => Proxy f -> a -> Property pureIsSame (_ :: Proxy f) (x :: a) = label "pure is same" $ (pure x :: f a) === Prelude.pure x seqRightIsSame :: (Applicative f ,Prelude.Applicative f ,Suitable f b ,Eq (f b) ,Show (f b)) => f a -> f b -> Property seqRightIsSame xs ys = label "*> is same" $ (xs *> ys) === (xs Prelude.*> ys) seqLeftIsSame :: (Applicative f ,Prelude.Applicative f ,Suitable f b ,Eq (f b) ,Show (f b)) => f b -> f a -> Property seqLeftIsSame xs ys = label "<* is same" $ (xs <* ys) === (xs Prelude.<* ys) applyIsSame :: (Applicative f ,Prelude.Applicative f ,Suitable f b ,Eq (f b) ,Show (f b)) => Blind (f (a -> b)) -> f a -> Property applyIsSame (Blind fs) xs = label "<*> is same" $ (fs <*> xs) === (fs Prelude.<*> xs) liftA2IsSame :: (Applicative f ,Prelude.Applicative f ,Suitable f c ,Eq (f c) ,Show (f c)) => Blind (a -> b -> c) -> f a -> f b -> Property liftA2IsSame (Blind f) xs ys = label "liftA2 is same" $ liftA2 f xs ys === Control.Applicative.liftA2 f xs ys bindIsSame :: (Monad f, Prelude.Monad f, Suitable f b, Show (f b), Eq (f b)) => f a -> Blind (a -> f b) -> Property bindIsSame xs (Blind f) = label ">>= is same" $ (xs >>= f) === (xs Prelude.>>= f) checkSame :: (Monad f ,Prelude.Monad f ,Show (f b) ,Show (f c) ,CoArbitrary c ,Arbitrary (f c) ,Arbitrary (f a) ,Arbitrary (f (b -> a)) ,CoArbitrary b ,Arbitrary a ,Arbitrary (f b) ,Suitable f a ,Eq (f a) ,Show (f a) ,Show a) => Proxy f -> Proxy a -> Proxy b -> Proxy c -> IO () checkSame (_ :: Proxy f) (_ :: Proxy a) (_ :: Proxy b) (_ :: Proxy c) = do quickCheck (fmapIsSame @ f @ a @ b) quickCheck (replaceIsSame @ f @ a @ b) quickCheck (pureIsSame (Proxy :: Proxy f) :: a -> Property) quickCheck (seqRightIsSame @ f @ a @ b) quickCheck (seqLeftIsSame @ f @ a @ b) quickCheck (applyIsSame @ f @ a @ b) quickCheck (liftA2IsSame @ f @ a @ b @ c) quickCheck (bindIsSame @ f @ a @ b) checkConstrained :: (Show (f a) ,Arbitrary (f a) ,Suitable f a ,Eq (f a) ,Show (f c) ,CoArbitrary c ,CoArbitrary b ,Arbitrary a ,Arbitrary (f c) ,Suitable f b ,Show (f b) ,Arbitrary (f b) ,Show a ,CoArbitrary a ,Eq (f b) ,Monad f ,Arbitrary b) => Proxy f -> Proxy a -> Proxy b -> Proxy c -> IO () checkConstrained (_ :: Proxy f) (_ :: Proxy a) (_ :: Proxy b) (_ :: Proxy c) = do quickCheck (fmapLaw @ f @ a) quickCheck (fmapCompLaw @ f @ a @ b @ c) quickCheck (seqRightLaw @ f @ a @ b) quickCheck (seqLeftLaw @ f @ a @ b) quickCheck (monadLawOne @ f @ a @ b) quickCheck (monadLawTwo @ f @ a) quickCheck (monadLawThree @ f @ a @ b @ c) checkUnConstrained :: (Show (f a) ,Arbitrary (f a) ,Suitable f a ,Suitable f ((a -> b) -> (c -> a) -> c -> b) ,Arbitrary (f (a -> b)) ,Arbitrary (f (c -> a)) ,Suitable f ((c -> a) -> c -> b) ,Suitable f (c -> b) ,Suitable f (a -> b) ,Suitable f ((a -> b) -> b) ,Eq (f a) ,Show (f c) ,CoArbitrary c ,CoArbitrary b ,Arbitrary a ,Arbitrary (f c) ,Suitable f b ,Show (f b) ,Arbitrary (f b) ,Show a ,CoArbitrary a ,Eq (f b) ,Monad f ,Suitable f (a -> a) ,Arbitrary b) => Proxy f -> Proxy a -> Proxy b -> Proxy c -> IO () checkUnConstrained (pf :: Proxy f) (pa :: Proxy a) (pb :: Proxy b) (pc :: Proxy c) = do checkConstrained pf pa pb pc quickCheck (appIdLaw @ f @ a) quickCheck (appCompLaw @ f @ a @ b @ c) quickCheck (homomorphismLaw (Proxy :: Proxy f) :: Blind (a -> b) -> a -> Property) quickCheck (interchangeLaw @ f @ a @ b) {-# ANN fmapLaw "HLint: ignore Functor law" #-} fmapLaw :: (Functor f, Suitable f a, Eq (f a), Show (f a)) => f a -> Property fmapLaw xs = label "fmap law" $ fmap id xs === xs {-# ANN fmapCompLaw "HLint: ignore Functor law" #-} fmapCompLaw :: (Functor f, Suitable f c, Eq (f c), Show (f c), Suitable f b) => Blind (b -> c) -> Blind (a -> b) -> f a -> Property fmapCompLaw (Blind f) (Blind g) xs = label "fmap comp law" $ fmap (f . g) xs === (fmap f . fmap g) xs seqRightLaw :: (Applicative f, Suitable f b, Eq (f b), Show (f b)) => f a -> f b -> Property seqRightLaw xs ys = label "*> law" $ (xs *> ys) === (liftA2 (const id) xs ys) seqLeftLaw :: (Applicative f, Suitable f a, Eq (f a), Show (f a)) => f a -> f b -> Property seqLeftLaw xs ys = label "<* law" $ (xs <* ys) === (liftA2 const xs ys) appIdLaw :: (Applicative f, Suitable f a, Suitable f (a -> a), Eq (f a), Show (f a)) => f a -> Property appIdLaw xs = label "app id law" $ (pure id <*> xs) === xs appCompLaw :: (Applicative f ,Suitable f ((b -> c) -> (a -> b) -> a -> c) ,Suitable f ((a -> b) -> a -> c) ,Suitable f (a -> c) ,Suitable f c ,Suitable f b ,Eq (f c) ,Show (f c)) => Blind (f (b -> c)) -> Blind (f (a -> b)) -> f a -> Property appCompLaw (Blind u) (Blind v) w = label "app comp law" $ (pure (.) <*> u <*> v <*> w) === (u <*> (v <*> w)) homomorphismLaw :: (Suitable f (a -> b) ,Suitable f b ,Suitable f a ,Applicative f ,Eq (f b) ,Show (f b)) => Proxy f -> Blind (a -> b) -> a -> Property homomorphismLaw (_ :: Proxy f) (Blind (f :: a -> b)) x = label "homomorphism law" $ (pure f <*> pure x) === (pure (f x) :: f b) interchangeLaw :: (Applicative f ,Suitable f a ,Suitable f b ,Suitable f ((a -> b) -> b) ,Eq (f b) ,Show (f b)) => Blind (f (a -> b)) -> a -> Property interchangeLaw (Blind u) y = label "interchange law" $ (u <*> pure y) === (pure ($y) <*> u) monadLawOne :: (Monad f, Suitable f a, Suitable f b, Show (f b), Eq (f b)) => Blind (a -> f b) -> a -> Property monadLawOne (Blind k) a = label "monad law one" $ (pure a >>= k) === k a monadLawTwo :: (Monad f, Suitable f a, Eq (f a), Show (f a)) => f a -> Property monadLawTwo xs = label "monad law two" $ (xs >>= pure) === xs monadLawThree :: (Monad f, Suitable f c, Eq (f c), Suitable f b, Show (f c)) => f a -> Blind (a -> f b) -> Blind (b -> f c) -> Property monadLawThree m (Blind k) (Blind h) = label "monad law three" $ (m >>= (k >=> h)) === ((m >>= k) >>= h) instance (Enum a, Bounded a, Eq1 m, Eq b) => Eq (ReaderT a m b) where ReaderT fs == ReaderT gs = all (\x -> eq1 (fs x) (gs x)) [minBound..maxBound] instance (CoArbitrary a, Arbitrary (m b)) => Arbitrary (ReaderT a m b) where arbitrary = fmap ReaderT arbitrary instance (CoArbitrary a, Arbitrary (m (b,a))) => Arbitrary (StateT a m b) where arbitrary = fmap StateT arbitrary instance (Enum a, Bounded a, Eq (m (b,a))) => Eq (StateT a m b) where StateT fs == StateT gs = all (\x -> (fs x) == (gs x)) [minBound..maxBound] instance (Enum a, Show a, Show (m b), Bounded a) => Show (ReaderT a m b) where show (ReaderT xs) = show (map ((,) <*> xs) [minBound..maxBound]) instance (Enum a, Show a, Show (m (b,a)), Bounded a) => Show (StateT a m b) where show (StateT xs) = show (map ((,) <*> xs) [minBound..maxBound]) instance Arbitrary a => Arbitrary (Identity a) where arbitrary = fmap Identity arbitrary main :: IO () main = do putStrLn "[]" checkSame (Proxy @ [] ) (Proxy @ Integer) (Proxy @ Word) (Proxy @ Int) checkUnConstrained (Proxy @ [] ) (Proxy @ Integer) (Proxy @ Word) (Proxy @ Int) putStrLn "Set" checkConstrained (Proxy @ Set) (Proxy @ Integer) (Proxy @ Word) (Proxy @ Int) putStrLn "IntSet" checkConstrained (Proxy @ IntSet) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) putStrLn "Reader Bool" checkUnConstrained (Proxy @ (Reader Bool)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) checkSame (Proxy @ (Reader Bool)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) putStrLn "ReaderT Bool Maybe" checkUnConstrained (Proxy @ (ReaderT Bool Maybe)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) checkSame (Proxy @ (ReaderT Bool Maybe)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) putStrLn "State Bool" checkUnConstrained (Proxy @ (State Bool)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) checkSame (Proxy @ (State Bool)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) putStrLn "StateT Bool Maybe" checkUnConstrained (Proxy @ (StateT Bool Maybe)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) checkSame (Proxy @ (StateT Bool Maybe)) (Proxy @ Int ) (Proxy @ Int ) (Proxy @ Int) doctest [ "-isrc", "src/" ]