{-# LANGUAGE TupleSections #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE DerivingVia #-} {-# OPTIONS_GHC -Wno-orphans #-} module Main (main) where import Control.Monad (unless) import System.Exit (exitFailure) import Control.Applicative (Const(..), ZipList(..)) import Hedgehog hiding (Var) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Expr import Control.Applicative.Trans.FreeAp -- Generators type GenExpr f = forall a. Gen (f (Expr a)) genList :: GenExpr [] genList = Gen.list (Range.linear 0 4) genVar descList :: [a] -> String descList as = "List(" ++ show (length as) ++ ")" genZipList :: GenExpr ZipList genZipList = ZipList <$> genList descZipList :: ZipList a -> String descZipList as = "ZipList(" ++ show (length as) ++ ")" genMaybe :: GenExpr Maybe genMaybe = Gen.maybe genVar descMaybe :: Maybe a -> String descMaybe = maybe "Nothing" (const "Just _") genStr :: Gen String genStr = Gen.string (Range.linear 0 4) (Gen.element "abc") genConst :: GenExpr (Const String) genConst = Const <$> genStr descConst :: Show c => Const c a -> String descConst = show genWriter :: GenExpr ((,) String) genWriter = (,) <$> genStr <*> genVar descWriter :: Show m => (m, a) -> String descWriter (m, _) = "(" ++ show m ++ ",_)" genApT :: GenExpr f -> GenExpr g -> GenExpr (ApT f g) genApT genF genG = Gen.choice [gen0, gen1, gen2] where cons genRest = do x <- genVar ga <- genG fb <- genF rc <- genRest pure $ ApT (\a b c -> x `appE` a `appE` b `appE` c) ga fb rc gen0 = PureT <$> genG gen1 = cons gen0 gen2 = cons gen1 descApT :: (forall x. f x -> String) -> (forall x. g x -> String) -> ApT f g a -> String descApT descF descG as = "ApT[" ++ foldApT_ (\fx -> "," ++ descF fx ++ ",") descG as ++ "]" -- Testing Applicatives law_Applicative :: (Applicative g, forall c. Eq (g (Expr c)), forall c. Show (g (Expr c))) => GroupName -> GenExpr g -> Group law_Applicative name genG = Group name [ ("left unit", left_unit) , ("right unit", right_unit) , ("associativity", assoc) ] where left_unit = property $ do gx <- forAll genG (pure id <*> gx) === gx right_unit = property $ do gx <- forAll genG y <- forAll genVar (gx |<*>| pure y) === fmap (`appE` y) gx assoc = property $ do x <- forAll genG y <- forAll genG z <- forAll genG (x |<*>| (y |<*>| z)) === (dotE <$> x <*> y <*> z) dotE f g a = appE f (appE g a) law_Applicative_hom :: (Applicative f, Applicative g, forall c. Show (f (Expr c)), forall c. Eq (g (Expr c)), forall c. Show (g (Expr c))) => (forall x. f x -> g x) -> GenExpr f -> Property law_Applicative_hom fg genF = property $ do x <- forAll $ genF y <- forAll $ genF (fg x |<*>| fg y) === fg (x |<*>| y) -- Lawful Applicative (ApT [] (Writer String)) grp_List_Writer_Applicative_laws :: Group grp_List_Writer_Applicative_laws = law_Applicative "ApT [] (Writer String) - Applicative laws" (genApT genList genWriter) -- Applicative (ApT [] []) grp_List_List_Applicative_laws :: Group grp_List_List_Applicative_laws = law_Applicative "ApT [] [] - Applicative laws" (genApT genList genList) -- Applicative (ApT [] ZipList) grp_List_ZipList_Applicative_laws :: Group grp_List_ZipList_Applicative_laws = law_Applicative "ApT [] ZipList - Applicative laws" (genApT genList genZipList) -- | liftT :: [] ~> ApT [] [] prop_hom_liftT_List :: Property prop_hom_liftT_List = law_Applicative_hom @[] @(ApT [] []) liftT genList -- | liftT :: (,) String ~> ApT [] ((,) String) prop_hom_liftT_Writer :: Property prop_hom_liftT_Writer = law_Applicative_hom @((,) String) @(ApT [] ((,) String)) liftT genWriter -- | foldApT id id :: ApT [] [] ~> [] prop_hom_foldApT_List_List_List :: Property prop_hom_foldApT_List_List_List = law_Applicative_hom (foldApT id id) (genApT genList genList) -- | foldApT ZipList id :: ApT [] ZipList ~> ZipList prop_hom_foldApT_List_ZipList_ZipList :: Property prop_hom_foldApT_List_ZipList_ZipList = law_Applicative_hom (foldApT ZipList id) (genApT genList genZipList) -- | foldApT showLen forgetData :: ApT [] (Writer String) -> Const String prop_hom_foldApT_List_Writer_Const :: Property prop_hom_foldApT_List_Writer_Const = law_Applicative_hom (foldApT showLen forgetData) (genApT genList genWriter) law_foldApT_liftF_liftT :: (Traversable f, Eq1 f, Show (f Int), Traversable g, Eq1 g, Show (g Int)) => Applicative g => GenExpr (ApT f g) -> Property law_foldApT_liftF_liftT gen = property $ do x <- forAll gen foldApT liftF liftT x === x prop_foldApT_liftF_liftT_List_List :: Property prop_foldApT_liftF_liftT_List_List = law_foldApT_liftF_liftT (genApT genList genList) prop_foldApT_liftF_liftT_Maybe_Writer :: Property prop_foldApT_liftF_liftT_Maybe_Writer = law_foldApT_liftF_liftT (genApT genMaybe genWriter) prop_foldApT_liftF_liftT_Writer_ZipList :: Property prop_foldApT_liftF_liftT_Writer_ZipList = law_foldApT_liftF_liftT (genApT genWriter genZipList) -- | Not an applicative hom showLen :: Foldable f => f a -> Const String a showLen = Const . show . length -- | An applicative hom forgetData :: (m, x) -> Const m x forgetData (m,_) = Const m shouldSuccess :: IO Bool -> IO () shouldSuccess m = m >>= \result -> unless result exitFailure main :: IO () main = do shouldSuccess $ checkParallel grp_List_Writer_Applicative_laws shouldSuccess $ checkParallel grp_List_List_Applicative_laws shouldSuccess $ checkParallel grp_List_ZipList_Applicative_laws shouldSuccess $ checkParallel $$(discover) -- Orphan instance deriving via [] instance Eq1 ZipList