{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverlappingInstances #-} -- | Conctrete tests of some instances od the 'Monoid' laws (for -- 'Endo', mainly). The laws themselves are one level up in the module -- hierarchy: 'Data.Monoid.Laws.defaultMonoidLaw1' etc. module Data.Monoid.Laws.Instances where import Data.Monoid (Monoid(mappend, mempty), Endo(Endo), appEndo) import Data.Monoid.Laws (MonoidLaws(..), MonoidLaw1, MonoidLaw2, MonoidLaw3) import Test.ClassLaws ( Equal, Law, quickLawCheck, lawtest, Property, quickCheck , Partial(Partial), unPartial , ArbitraryPartial(arbitraryPartial), SemanticEq((==!), semanticEq), SemanticOrd , quickLawCheckPartial ) import Test.ClassLaws.TestingDatatypes (MyList(..), (+++)) import Test.ClassLaws.TestingFinFuns(arbitraryPartialFun, showPartialFun, eqPartial, semEqFun) import Control.Monad (liftM) import Data.List(intersperse) instance MonoidLaws (Endo a) -- | Cheating: just showing a few values (@map f [0..10]@). instance Show (Endo Int) where show (Endo f) = "E("++(concat $ intersperse "," $ map (show . f) [0..10])++")" testMonoidEndo = do quickLawCheck (undefined::MonoidLaw1 (Endo Bool)) quickLawCheck (undefined::MonoidLaw2 (Endo Bool)) quickLawCheck (undefined::MonoidLaw3 (Endo Bool)) instance (Bounded a, Enum a, Show (Partial a)) => Show (Partial (Endo a)) where show (Partial (Endo e)) = showPartialFun e instance (Bounded a, Enum a, SemanticOrd a, ArbitraryPartial a) => ArbitraryPartial (Endo a) where arbitraryPartial = liftM Endo (arbitraryPartialFun arbitraryPartial) instance (Bounded a, Enum a, Eq a) => Eq (Endo a) where (Endo f) == (Endo g) = f == g {- -- Alternative definition, needs -- {-# LANGUAGE UndecidableInstances #-} instance SemanticEq (a->a) => SemanticEq (Endo a) where semanticEq tweak (Endo f) (Endo g) = semanticEq tweak f g -} instance (Bounded a, Enum a, SemanticEq a) => SemanticEq (Endo a) where semanticEq tweak (Endo f) (Endo g) = semEqFun semanticEq tweak f g testMonoidEndoPartial = do quickLawCheckPartial (undefined::MonoidLaw1 (Endo Bool)) -- expected failure quickLawCheckPartial (undefined::MonoidLaw2 (Endo Bool)) -- expected failure quickLawCheckPartial (undefined::MonoidLaw3 (Endo Bool)) {- The following Monoid instance for MyList does *not* satisfy the Monoid laws. -} instance Monoid (MyList a) where mempty = Nil mappend xs ys = xs +++ ys +++ xs instance MonoidLaws (MyList a) testMonoidMyList = do quickLawCheck (undefined :: MonoidLaw1 (MyList Int)) quickLawCheck (undefined :: MonoidLaw2 (MyList Int)) quickLawCheck (undefined :: MonoidLaw3 (MyList Int)) main = do testMonoidEndo testMonoidMyList -- expected failures -- ================================================================ -- Just for fun: Endo Bool is also finite and bounded ... instance Bounded (Endo Bool) where minBound = Endo (const False) maxBound = Endo (const True) instance Enum (Endo Bool) where fromEnum (Endo f) = 2*fromEnum (f False) + fromEnum (f True) toEnum n = Endo (\b->if b then toEnum(n`mod`2) else toEnum (n`div`2)) -- cheating: should really check if n is within 0..3 b2i :: Bool -> Int b2i = fromEnum instance Show (Endo Bool) where show (Endo f) = 'E':concatMap (show.b2i.f) [False,True] test_roundtrip :: Bool test_roundtrip = (toEnum :: Int -> Endo Bool) . (fromEnum :: Endo Bool -> Int) == id