ClassLaws (empty) → 0.3.0.0
raw patch · 16 files changed
+1517/−0 lines, 16 filesdep +ChasingBottomsdep +QuickCheckdep +basesetup-changed
Dependencies added: ChasingBottoms, QuickCheck, base, mtl
Files
- ClassLaws.cabal +86/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- src/Control/Monad/Laws.hs +152/−0
- src/Control/Monad/Laws/Instances.hs +176/−0
- src/Control/Monad/State/Class/Laws.hs +65/−0
- src/Control/Monad/State/Class/Laws/Instances.hs +86/−0
- src/Data/Monoid/Laws.hs +43/−0
- src/Data/Monoid/Laws/Instances.hs +99/−0
- src/Test/ClassLaws.hs +13/−0
- src/Test/ClassLaws/Core.hs +74/−0
- src/Test/ClassLaws/Partial.hs +108/−0
- src/Test/ClassLaws/TestingDatatypes.hs +79/−0
- src/Test/ClassLaws/TestingEquality.hs +87/−0
- src/Test/ClassLaws/TestingFinFuns.hs +141/−0
- src/Test/ClassLaws/TestingState.hs +276/−0
+ ClassLaws.cabal view
@@ -0,0 +1,86 @@+-- Initial ClassLaws.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++-- The name of the package.+name: ClassLaws++-- The package version. See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary: +-+------- breaking API changes+-- | | +----- non-breaking API additions+-- | | | +--- code changes with no API change+version: 0.3.0.0++-- A short (one-line) description of the package.+synopsis: Stating and checking laws for type class methods++homepage: http://wiki.portal.chalmers.se/cse/pmwiki.php/FP/ClassLaws++-- A longer description of the package.++description: The specification of a class in Haskell often starts with+ stating, in text, the laws that should be satisfied by methods+ defined in instances of the class, followed by the type of the+ methods of the class. The ClassLaws library is a framework that+ supports testing such class laws using QuickCheck. Our framework is+ a light-weight class law testing framework, which requires a limited+ amount of work per class law, and per datatype for which the class+ law is tested. We also show how to test class laws with+ partially-defined values. Using partially-defined values, we show+ that the standard lazy and strict implementations of the state monad+ do not satisfy the expected laws. More information can be found at+ http://wiki.portal.chalmers.se/cse/pmwiki.php/FP/ClassLaws++-- The license under which the package is released.+license: BSD3++-- The file containing the license text.+license-file: LICENSE++-- The package author(s).+author: Patrik Jansson and Johan Jeuring++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer: patrikj@chalmers.se++-- A copyright notice.+-- copyright: ++category: Testing++build-type: Simple++-- Constraint on the version of Cabal needed to build this package.+cabal-version: >=1.8+++library+ -- Modules exported by the library.+ exposed-modules: + Test.ClassLaws, Test.ClassLaws.Core, Test.ClassLaws.TestingEquality, Test.ClassLaws.Partial, + Test.ClassLaws.TestingFinFuns, + Test.ClassLaws.TestingDatatypes, Test.ClassLaws.TestingState,+ Control.Monad.Laws, Control.Monad.Laws.Instances, + Control.Monad.State.Class.Laws, Control.Monad.State.Class.Laws.Instances, + Data.Monoid.Laws, Data.Monoid.Laws.Instances+ + -- The Control.Monad and Data.Monoid laws should perhaps be split off + -- Some modules may be made internal ++ -- Modules included in this library but not exported.+ -- other-modules: +-- Test.ClassLaws.Tests, ++ + -- Other library packages from which modules are imported.+ build-depends: + base >=4.5 && < 5, + mtl >= 1 && < 3, + QuickCheck >= 2 && < 3, + ChasingBottoms >=1.3 && < 2+ + -- Directories containing source files.+ hs-source-dirs: src+
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Patrik Jansson and Johan Jeuring++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Patrik Jansson and Johan Jeuring nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Control/Monad/Laws.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE EmptyDataDecls #-}++{- | This module implements the laws in Control.Monad, specified in+the Haskell 2010 report, in 6.3.5 for Functor, in 6.3.6 for Monad, and+in Chapter 13, module Control.Monad. -}+module Control.Monad.Laws (module Test.ClassLaws, module Control.Monad.Laws) where+import Control.Monad -- For MonadPlus; Functor and Monad are both in the Prelude+import Test.ClassLaws (LawTest(lawtest), LawArgs, LawBody, Law, (=.=), TestEqual, testEqual)++data FunctorLaw1 a (f :: * -> *) +data FunctorLaw2 a b c (f :: * -> *) ++class Functor f => FunctorLaws f where+ + functorLaw1 :: Law (FunctorLaw1 a f)+ functorLaw2 :: Law (FunctorLaw2 a b c f)++ functorLaw1 = defaultFunctorLaw1+ functorLaw2 = defaultFunctorLaw2++defaultFunctorLaw1 x = fmap id x =.= id x+defaultFunctorLaw2 (f,g,x) = (fmap f . fmap g) x =.= fmap (f . g) x++type instance LawArgs (FunctorLaw1 a f) = f a+type instance LawBody (FunctorLaw1 a f) = f a++type instance LawArgs (FunctorLaw2 a b c f) = (b -> c, a -> b, f a)+type instance LawBody (FunctorLaw2 a b c f) = f c++instance (FunctorLaws f, TestEqual (f a)) => LawTest (FunctorLaw1 a f) where+ lawtest _ = testEqual . functorLaw1+ +instance (FunctorLaws f, TestEqual (f c)) => LawTest (FunctorLaw2 a b c f) where+ lawtest _ = testEqual . functorLaw2+++data MonadLaw1 a b (m :: * -> *)+data MonadLaw2 b (m :: * -> *)+data MonadLaw3 b c d (m :: * -> *)++class Monad m => MonadLaws m where+ + monadLaw1 :: Law (MonadLaw1 a b m)+ monadLaw2 :: Law (MonadLaw2 b m)+ monadLaw3 :: Law (MonadLaw3 b c d m)+ + monadLaw1 = defaultMonadLaw1 + monadLaw2 = defaultMonadLaw2+ monadLaw3 = defaultMonadLaw3++defaultMonadLaw1 (a,k) = return a >>= k =.= k a+defaultMonadLaw2 m = m >>= return =.= m+defaultMonadLaw3 (m,k,h) = m >>= (\x -> k x >>= h) =.= (m >>= k) >>= h++type instance LawArgs (MonadLaw1 a b m) = (a, a -> m b) +type instance LawBody (MonadLaw1 a b m) = m b++type instance LawArgs (MonadLaw2 b m) = m b+type instance LawBody (MonadLaw2 b m) = m b++type instance LawArgs (MonadLaw3 b c d m) = (m b, b -> m c, c -> m d)+type instance LawBody (MonadLaw3 b c d m) = m d++instance (MonadLaws m,TestEqual (m b)) => LawTest (MonadLaw1 a b m) where+ lawtest _ = testEqual . monadLaw1+ +instance (MonadLaws m,TestEqual (m b)) => LawTest (MonadLaw2 b m) where+ lawtest _ = testEqual . monadLaw2++instance (MonadLaws m,TestEqual (m d)) => LawTest (MonadLaw3 b c d m) where+ lawtest _ = testEqual . monadLaw3+++data FunctorMonadLaw a b (m :: * -> *)++class (Functor m, Monad m) => FunctorMonadLaws m where++ functorMonadLaw :: Law (FunctorMonadLaw a b m)++ functorMonadLaw = defaultFunctorMonadLaw ++defaultFunctorMonadLaw (f,xs) = fmap f xs =.= xs >>= return . f+++type instance LawArgs (FunctorMonadLaw a b m) = (a -> b, m a) +type instance LawBody (FunctorMonadLaw a b m) = m b+ +instance (FunctorMonadLaws m,TestEqual (m b)) => LawTest (FunctorMonadLaw a b m) where+ lawtest _ = testEqual . functorMonadLaw+++{- | The laws for MonadPlus are less prominently declared in the base+libraries. -}++data MonadPlusLaw1 a (m :: * -> *)+data MonadPlusLaw2 a (m :: * -> *)+data MonadPlusLaw3 a b (m :: * -> *)+data MonadPlusLaw4 a (m :: * -> *)+data MonadPlusLaw5 a (m :: * -> *)++class MonadPlus m => MonadPlusLaws m where++ monadPlusLaw1 :: Law (MonadPlusLaw1 a m)+ monadPlusLaw2 :: Law (MonadPlusLaw2 a m)+ monadPlusLaw3 :: Law (MonadPlusLaw3 a b m)+ monadPlusLaw4 :: Law (MonadPlusLaw4 a m)+ monadPlusLaw5 :: Law (MonadPlusLaw5 a m)+ + monadPlusLaw1 = defaultMonadPlusLaw1+ monadPlusLaw2 = defaultMonadPlusLaw2+ monadPlusLaw3 = defaultMonadPlusLaw3+ monadPlusLaw4 = defaultMonadPlusLaw4+ monadPlusLaw5 = defaultMonadPlusLaw5++defaultMonadPlusLaw1 x = mzero `mplus` x =.= x +defaultMonadPlusLaw2 x = x `mplus` mzero =.= x+defaultMonadPlusLaw3 f = mzero >>= f =.= mzero+defaultMonadPlusLaw4 v = v >> mzero =.= mzero+defaultMonadPlusLaw5 (a,b,c) = a `mplus` (b `mplus` c) =.= (a `mplus` b) `mplus` c++type instance LawArgs (MonadPlusLaw1 a m) = m a +type instance LawBody (MonadPlusLaw1 a m) = m a++type instance LawArgs (MonadPlusLaw2 a m) = m a +type instance LawBody (MonadPlusLaw2 a m) = m a++type instance LawArgs (MonadPlusLaw3 a b m) = a -> m b +type instance LawBody (MonadPlusLaw3 a b m) = m b+ +type instance LawArgs (MonadPlusLaw4 a m) = m a +type instance LawBody (MonadPlusLaw4 a m) = m a++type instance LawArgs (MonadPlusLaw5 a m) = (m a, m a, m a)+type instance LawBody (MonadPlusLaw5 a m) = m a++instance (MonadPlusLaws m,TestEqual (m a)) => LawTest (MonadPlusLaw1 a m) where+ lawtest _ = testEqual . monadPlusLaw1++instance (MonadPlusLaws m,TestEqual (m a)) => LawTest (MonadPlusLaw2 a m) where+ lawtest _ = testEqual . monadPlusLaw2 ++instance (MonadPlusLaws m,TestEqual (m b)) => LawTest (MonadPlusLaw3 a b m) where+ lawtest _ = testEqual . monadPlusLaw3++instance (MonadPlusLaws m,TestEqual (m a)) => LawTest (MonadPlusLaw4 a m) where+ lawtest _ = testEqual . monadPlusLaw4++instance (MonadPlusLaws m,TestEqual (m a)) => LawTest (MonadPlusLaw5 a m) where+ lawtest _ = testEqual . monadPlusLaw5
+ src/Control/Monad/Laws/Instances.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}++-- | Tests the Monad ClassLaws for a few example datatypes. Mainly+-- instance declarations and QuickCheck tests + a 'main' to run it.+module Control.Monad.Laws.Instances where+import Control.Monad.State+import Control.Monad.Laws+import Test.ClassLaws+import Test.ClassLaws.TestingDatatypes(MyList(..), (+++), snoc, foldrMyList)++instance FunctorLaws [] where+ functorLaw1 xs = addSteps (defaultFunctorLaw1 xs)+ (case xs of+ [] -> nilCase + ys@(_:_) -> conCase ys)+ where+ nilCase = + [ fmap id [] + , -- definition of fmap on []+ []+ ]+ conCase (y:ys) = + [ fmap id (y:ys)+ , -- definition of fmap on (x:xs)+ id y:fmap id ys+ , -- definition of id+ y:fmap id ys +-- y:fmap id (ys++ys) -- gives an error (used to test error injection)+ , -- induction hypothesis+ y:ys+ , -- definition of id+ id (y:ys)+ ]++testFunctorList+ = do quickLawCheck (undefined::FunctorLaw1 Char [])+ quickFLawCheck (undefined::FunctorLaw2 Int Char Bool [])++instance FunctorLaws Maybe++testFunctorMaybe + = do quickLawCheck (undefined::FunctorLaw1 Char Maybe)+ quickFLawCheck (undefined::FunctorLaw2 Int Char Bool Maybe)+++instance FunctorLaws IO++{- -- How do I test IO values?++testFunctorIO + = do quickBlind (undefined::FunctorLaw1 Char IO)+ quickBlind (undefined::FunctorLaw2 Int Char Bool IO)+-}+++{- +The following instance of Functor for MyList should *not* satisfy the functor+laws.+-}++-- Wrong instance of functor, because the order is reversed by fmap.++instance Functor MyList where+ fmap f Nil = Nil+ fmap f (Cons x xs) = snoc (f x) (fmap f xs)++instance FunctorLaws MyList+ where+ functorLaw1 xs = addSteps (defaultFunctorLaw1 xs)+ (case xs of+ Nil -> nilCase + zs@(Cons y ys) -> conCase zs)+ where+ nilCase = + [ fmap id Nil + , -- definition of fmap on []+ Nil+ ]+ conCase (Cons y ys) = + [ fmap id (Cons y ys)+ , -- definition of fmap on (x:xs)+ snoc (id y) (fmap id ys)+ , -- definition of id+ snoc y (fmap id ys)+ , -- induction hypothesis+ snoc y ys+ , -- definition of id+ id (Cons y ys)+ ]++testFunctorMyList+ = do quickLawCheck (undefined::FunctorLaw1 Int MyList)+ quickFLawCheck (undefined::FunctorLaw2 Char Int Int MyList)+++instance MonadLaws [] ++testMonadList+ = do quickFLawCheck (undefined::MonadLaw1 Char Int [])+ quickLawCheck (undefined::MonadLaw2 Int [])+ quickFLawCheck (undefined::MonadLaw3 Int Bool Char [])+++instance MonadLaws Maybe ++testMonadMaybe+ = do quickFLawCheck (undefined::MonadLaw1 Char Int Maybe)+ quickLawCheck (undefined::MonadLaw2 Int Maybe)+ quickFLawCheck (undefined::MonadLaw3 Int Bool Char Maybe)+++instance FunctorMonadLaws MyList++testFunctorMonadMyList+ = do quickFLawCheck (undefined:: FunctorMonadLaw Char Int MyList)+++instance MonadLaws IO+++instance MonadLaws (State s)++testMonadState+ = do quickFLawCheck (undefined::MonadLaw1 Bool Int (State Bool))+ quickFLawCheck (undefined::MonadLaw2 Int (State Bool)) -- necessary because of Show State problem+ quickFLawCheck (undefined::MonadLaw3 Int Bool Char (State Bool))+++instance Monad MyList where+ m >>= k = foldrMyList ((+++) . k) Nil m+ m >> k = foldrMyList ((+++) . (\ _ -> k)) Nil m+ return x = Cons x (Cons x Nil) -- gives an error+-- return x = Cons x Nil -- correct+ fail _ = Nil++instance MonadLaws MyList + +testMonadMyList+ = do quickFLawCheck (undefined::MonadLaw1 Char Int MyList)+ quickLawCheck (undefined::MonadLaw2 Int MyList)+ quickFLawCheck (undefined::MonadLaw3 Int Bool Char MyList)+++instance FunctorMonadLaws [] ++testFunctorMonadList+ = do quickFLawCheck (undefined::FunctorMonadLaw Char Int [])+++instance FunctorMonadLaws Maybe++testFunctorMonadMaybe+ = do quickFLawCheck (undefined::FunctorMonadLaw Char Int Maybe)+++instance FunctorMonadLaws IO+++main = do testMonadMaybe+ testMonadState++ testFunctorList+ testFunctorMaybe++ testFunctorMonadList+ testFunctorMonadMaybe++expectedFailures = do+ testMonadMyList + testFunctorMyList + testFunctorMonadMyList ++-- No MonadPlusLaw instances yet. +
+ src/Control/Monad/State/Class/Laws.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE EmptyDataDecls #-}++-- | Laws for the 'MonadState' class. A submodule has a+-- 'Control.Monad.State.Class.Laws.Instances.main' which runs quite a+-- few tests for the lazy and strict state monads.+module Control.Monad.State.Class.Laws where+import Control.Monad.State.Class (MonadState(..))+import Test.ClassLaws++class MonadState s m => MonadStateLaws s m where+ monadStatePutPut :: Law (MonadStatePutPut s m)+ monadStatePutGet :: Law (MonadStatePutGet s m)+ monadStateGetPut :: Law (MonadStateGetPut m)+ monadStateGetGet :: Law (MonadStateGetGet s a m)++ monadStatePutPut = defaultMonadStatePutPut+ monadStatePutGet = defaultMonadStatePutGet+ monadStateGetPut = defaultMonadStateGetPut+ monadStateGetGet = defaultMonadStateGetGet++defaultMonadStatePutPut (s,s') = put s' >> put s =.= put s+defaultMonadStatePutGet s = put s >> get =.= put s >> return s+defaultMonadStateGetPut _ = get >>= put =.= return ()+defaultMonadStateGetGet k = get >>= (\s->get >>= k s) =.= get >>= \s->k s s++data MonadStatePutPut s (m :: * -> *)+data MonadStatePutGet s (m :: * -> *)+data MonadStateGetPut (m :: * -> *)+data MonadStateGetGet s a (m :: * -> *)++type instance LawArgs (MonadStatePutPut s m) = (s, s) +type instance LawBody (MonadStatePutPut s m) = m ()++type instance LawArgs (MonadStatePutGet s m) = s+type instance LawBody (MonadStatePutGet s m) = m s++type instance LawArgs (MonadStateGetPut m) = () +type instance LawBody (MonadStateGetPut m) = m ()++type instance LawArgs (MonadStateGetGet s a m) = s -> s -> m a+type instance LawBody (MonadStateGetGet s a m) = m a+++instance (MonadStateLaws s m, TestEqual (m ())) =>+ LawTest (MonadStatePutPut s m) where+ lawtest _ = testEqual . (monadStatePutPut :: Law (MonadStatePutPut s m))+-- lawtest _ = testEqual . monadStatePutPut -- explicit type needed++instance (MonadStateLaws s m, TestEqual (m s)) =>+ LawTest (MonadStatePutGet s m) where+ lawtest _ = testEqual . (monadStatePutGet :: Law (MonadStatePutGet s m))++instance (MonadStateLaws s m, TestEqual (m ())) =>+ LawTest (MonadStateGetPut m) where+ lawtest _ = testEqual . (monadStateGetPut :: Law (MonadStateGetPut m))++instance (MonadStateLaws s m, TestEqual (m a)) =>+ LawTest (MonadStateGetGet s a m) where+ lawtest _ = testEqual . (monadStateGetGet :: Law (MonadStateGetGet s a m))
+ src/Control/Monad/State/Class/Laws/Instances.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- | Tests of the 'MonadState' laws for lazy and strict state monads.+-- The laws are one level up in the module hierarchy: +-- 'Control.Monad.State.Class.Laws.defaultMonadStatePutGet' etc.+module Control.Monad.State.Class.Laws.Instances where++import Control.Monad.Laws + ( MonadLaws, FunctorLaws, FunctorMonadLaws+ , MonadLaw1, MonadLaw2, MonadLaw3+ , FunctorLaw1, FunctorLaw2, FunctorMonadLaw+ )+import Control.Monad.State.Class.Laws++import Test.ClassLaws+import Test.ClassLaws.TestingState+++instance MonadStateLaws s (State s)+instance MonadLaws (State s)+instance FunctorLaws (State s)+instance FunctorMonadLaws (State s)+++instance MonadStateLaws s (SS s)+instance MonadLaws (SS s)+instance FunctorLaws (SS s)+instance FunctorMonadLaws (SS s)+----------------------------------------------------------------------++testLawsStateL = do+ quickLawCheck (undefined::MonadStatePutPut Bool (State Bool))+ quickLawCheck (undefined::MonadStatePutGet Bool (State Bool))+ quickLawCheck (undefined::MonadStateGetPut (State Bool))+ quickLawCheck (undefined::MonadStateGetGet Bool Ordering (State Bool))+ quickLawCheck (undefined::FunctorLaw1 () (State Bool))+ quickLawCheck (undefined::FunctorLaw2 Ordering Bool () (State Bool))+ quickLawCheck (undefined::MonadLaw1 Bool () (State Bool))+ quickLawCheck (undefined::MonadLaw2 Ordering (State Bool))+ quickLawCheck (undefined::MonadLaw3 () Ordering Bool (State Bool))+ quickLawCheck (undefined::FunctorMonadLaw () Ordering (State Bool))++testLawsStatePartialL = do+ quickLawCheckPartial (undefined::MonadStatePutPut Bool (State Bool))+ quickLawCheckPartial (undefined::MonadStatePutGet Bool (State Bool))+ quickLawCheckPartial (undefined::MonadStateGetPut (State Bool))+ quickLawCheckPartial (undefined::MonadStateGetGet Bool Ordering (State Bool))+ quickLawCheckPartial (undefined::FunctorLaw1 () (State Bool))+ quickLawCheckPartial (undefined::FunctorLaw2 Ordering Bool () (State Bool))+ quickLawCheckPartial (undefined::MonadLaw1 Bool () (State Bool))+ quickLawCheckPartial (undefined::MonadLaw2 Ordering (State Bool))+ quickLawCheckPartial (undefined::MonadLaw3 () Ordering Bool (State Bool))+ quickLawCheckPartial (undefined::FunctorMonadLaw () Ordering (State Bool))++testLawsStateS = do+ quickLawCheck (undefined::MonadStatePutPut Bool (SS Bool))+ quickLawCheck (undefined::MonadStatePutGet Bool (SS Bool))+ quickLawCheck (undefined::MonadStateGetPut (SS Bool))+ quickLawCheck (undefined::MonadStateGetGet Bool Ordering (SS Bool))+ quickLawCheck (undefined::FunctorLaw1 () (SS Bool))+ quickLawCheck (undefined::FunctorLaw2 Ordering Bool () (SS Bool))+ quickLawCheck (undefined::MonadLaw1 Bool () (SS Bool))+ quickLawCheck (undefined::MonadLaw2 Ordering (SS Bool))+ quickLawCheck (undefined::MonadLaw3 () Ordering Bool (SS Bool))+ quickLawCheck (undefined::FunctorMonadLaw () Ordering (SS Bool))++-- TODO: fix the class constraints problems (related to ChasingBottoms Data a => SemanticEq a instances)+testLawsStatePartialS = do+ quickLawCheckPartial (undefined::MonadStatePutPut Bool (SS Bool))+ quickLawCheckPartial (undefined::MonadStatePutGet Bool (SS Bool))+ quickLawCheckPartial (undefined::MonadStateGetPut (SS Bool))+ -- quickLawCheckPartial (undefined::MonadStateGetGet Bool Ordering (SS Bool))+ quickLawCheckPartial (undefined::FunctorLaw1 () (SS Bool))+ quickLawCheckPartial (undefined::FunctorLaw2 Ordering Bool () (SS Bool))+ -- quickLawCheckPartial (undefined::MonadLaw1 Bool () (SS Bool))+ quickLawCheckPartial (undefined::MonadLaw2 Ordering (SS Bool))+ -- quickLawCheckPartial (undefined::MonadLaw3 () Ordering Bool (SS Bool))+ quickLawCheckPartial (undefined::FunctorMonadLaw () Ordering (SS Bool))++main = do+ testLawsStateL+ testLawsStatePartialL+ testLawsStateS+ testLawsStatePartialS+
+ src/Data/Monoid/Laws.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE EmptyDataDecls #-}++-- | ClassLaws for the 'Monoid' class. Actual tests are defined in the Instances submodule and can be run from 'Data.Monoid.Laws.Instances.main'.+module Data.Monoid.Laws where+import Data.Monoid +import Test.ClassLaws++data MonoidLaw1 m +data MonoidLaw2 m +data MonoidLaw3 m++class Monoid m => MonoidLaws m where++ monoidLaw1 :: Law (MonoidLaw1 m)+ monoidLaw2 :: Law (MonoidLaw2 m)+ monoidLaw3 :: Law (MonoidLaw3 m)++ monoidLaw1 = defaultMonoidLaw1+ monoidLaw2 = defaultMonoidLaw2+ monoidLaw3 = defaultMonoidLaw3++defaultMonoidLaw1 m = m =.= m `mappend` mempty+defaultMonoidLaw2 m = m `mappend` mempty =.= m +defaultMonoidLaw3 (m1,m2,m3) = m1 `mappend` (m2 `mappend` m3) =.= (m1 `mappend` m2) `mappend` m3 ++type instance LawArgs (MonoidLaw1 m) = m+type instance LawBody (MonoidLaw1 m) = m++type instance LawArgs (MonoidLaw2 m) = m+type instance LawBody (MonoidLaw2 m) = m++type instance LawArgs (MonoidLaw3 m) = (m, m, m)+type instance LawBody (MonoidLaw3 m) = m++instance (MonoidLaws a, TestEqual a) => LawTest (MonoidLaw1 a) where+ lawtest _ = testEqual . monoidLaw1++instance (MonoidLaws a, TestEqual a) => LawTest (MonoidLaw2 a) where+ lawtest _ = testEqual . monoidLaw2++instance (MonoidLaws a, TestEqual a) => LawTest (MonoidLaw3 a) where+ lawtest _ = testEqual . monoidLaw3
+ src/Data/Monoid/Laws/Instances.hs view
@@ -0,0 +1,99 @@+{-# 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
+ src/Test/ClassLaws.hs view
@@ -0,0 +1,13 @@+-- | The central part of ClassLaws is defined in the .Core, .Partial+-- and .TestingEquality. Some more helper functions and examples+-- reside in Test.ClassLaws.*. Finally, laws for the Monoid, Monad and+-- MonadState classes live under their definitions in the hierarchy:+-- Data.Monoid.Laws, Control.Monad.Laws, etc.+module Test.ClassLaws ( module Test.ClassLaws.Core+ , module Test.ClassLaws.Partial+ , module Test.ClassLaws.TestingEquality+ , module Test.QuickCheck) where+import Test.ClassLaws.Core+import Test.ClassLaws.TestingEquality+import Test.ClassLaws.Partial+import Test.QuickCheck
+ src/Test/ClassLaws/Core.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}++-- | The core of ClassLaws are the type families 'LawArgs', 'LawBody' and+-- 'Param', tied together by the type class 'LawTest'. +module Test.ClassLaws.Core where+import Test.QuickCheck +import Test.ClassLaws.Partial++-- | An equality proof is represented as a list of (at least two) equal values.+type Equal = [] +-- | A Theorem is a claim that a LHS equals a RHS - an 'Equal' of length two.+type Theorem = Equal++infixr 0 =.=+-- | Contructing an equality theorem: @lhs =.= rhs = [lhs, rhs]@.+(=.=) :: a -> a -> Theorem a +lhs =.= rhs = [lhs, rhs]++-- | Take a two-element "theorem" and an equality proof chain to splice in the middle.+addSteps :: Theorem a -> Equal a -> Equal a+addSteps [lhs,rhs] steps = lhs : steps ++ [rhs]+addSteps _ _ = error "addSteps should only be used on two-element lists"+++-- | The forall quantified part on the top level of the law+type family LawArgs t +-- | The type in the body of the forall+type family LawBody t +-- | Parameters needed for 'Equal' checking of the body+type family Param b ++-- | The 'Law's we handle are of this form.+type Law t = LawArgs t -> Equal (LawBody t)++{- |+Class LawTest defines a test method, which returns a testable property, which we+can use to test a law for a type t. This class is independent of the actual laws+to test - it can be used for Monoid, Monad, ...+-} +class LawTest t where + lawtest :: t -> LawArgs t -> Param (LawBody t) -> Property++-- | Helper function to test laws where arguments lack a Show instance.+blindlawtest :: (LawTest t) => t -> Blind (LawArgs t) -> Param (LawBody t) -> Property+blindlawtest a (Blind f) = lawtest a f ++-- | Helper function to test laws where we should care about partial values.+partiallawtest :: (LawTest t) => t -> Partial ((LawArgs t) -> Param (LawBody t) -> Property)+partiallawtest a = Partial $ lawtest a++-- | Top level use of ClassLaws is often @'quickLawCheck' someLaw@+quickLawCheck ::+ (Show (LawArgs t), + Arbitrary (LawArgs t),+ Show (Param (LawBody t)), + Arbitrary (Param (LawBody t)), + LawTest t) =>+ t -> IO ()+quickLawCheck = quickCheck . lawtest+-- quickLawCheck law = quickCheck (lawtest law) -- alternative version does not need expl. type sig.+-- | Variant not needing a Show instance for the 'LawArg's+quickFLawCheck law = quickCheck (blindlawtest law)++-- | Checking laws in the precense of partial values+quickLawCheckPartial+ :: ( Show (Partial (Param (LawBody t)))+ , Show (Partial (LawArgs t))+ , ArbitraryPartial (Param (LawBody t))+ , ArbitraryPartial (LawArgs t)+ , LawTest t) =>+ t -> IO ()+quickLawCheckPartial = quickCheck . Partial . lawtest+
+ src/Test/ClassLaws/Partial.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}+-- | This module collects the infrastructure used to easily switch+-- between testing ClassLaws with or without partial values. Built+-- around QuickCheck and ChasingBottoms.+module Test.ClassLaws.Partial + ( module Test.ClassLaws.Partial+ , module Test.ChasingBottoms+ ) where+import Test.QuickCheck+import Test.ChasingBottoms hiding (Result, listOf) -- clash with QuickCheck++import Data.List (intersperse)+import Control.Monad (liftM2, liftM3)++-- | A modifier to indicate that partial values should be generated+-- (or tested, or both).+newtype Partial a = Partial {unPartial :: a}++instance TestablePartial prop => Testable (Partial prop) where+ property (Partial x) = propertyPartial x++-- | Declaring a property for possibly partial values.+class TestablePartial prop where+ propertyPartial :: prop -> Property++-- | We copy the QuickCheck structure to make sure generators of+-- partial values and equality checks handling partial values are+-- used.+class ArbitraryPartial a where+ arbitraryPartial :: Gen a++ shrinkPartial :: a -> [a]+ shrinkPartial _ = []+++instance TestablePartial Bool where+ propertyPartial = property++instance TestablePartial Property where+ propertyPartial = property++instance ( ArbitraryPartial a+ , Show (Partial a)+ , TestablePartial prop+ ) => TestablePartial (a -> prop) where+ propertyPartial f = forAllShrink arb shr prop+ where+ arb = fmap Partial arbitraryPartial+ shr (Partial x) = map Partial (shrinkPartial x) + prop (Partial x) = propertyPartial (f x)++--------------------------------------------------------------++-- | Helper for showing partial values+showPartial :: String -> (a -> String) -> a -> String+showPartial t _ p | isBottom p = "_|_" ++ t ++ "_"+showPartial _ f p = f p++instance Show (Partial ()) where+ show (Partial u) = showPartial "()" show u++instance Show (Partial Bool) where+ show (Partial b) = showPartial "Bool" show b++instance Show (Partial Char) where+ show (Partial c) = showPartial "Char" show c++instance Show (Partial Int) where+ show (Partial i) = showPartial "Int" show i++-- | Helper for generating partial values: @genPartial ib ia ga@+-- generates 'bottom' with frequence @ib@ and @ga@ with frequency+-- @ia@.+genPartial :: Int -> Int -> Gen a -> Gen a+genPartial ib ia ga = frequency [ (ib, return bottom), (ia, ga) ]++instance ArbitraryPartial Int where+ arbitraryPartial = genPartial 1 20 $ arbitrary++instance ArbitraryPartial Char where+ arbitraryPartial = genPartial 1 20 $ arbitrary++instance ArbitraryPartial Bool where+ arbitraryPartial = genPartial 1 10 $ arbitrary++instance ArbitraryPartial () where+ arbitraryPartial = genPartial 1 5 $ arbitrary++------------------------------------------------------------++instance (Show (Partial a), Show (Partial b)) => Show (Partial (a,b)) where+ show = showPartial "(,)" showPair+ where showPair (Partial (a,b)) = + "(" ++ show (Partial a) ++ "," + ++ show (Partial b) ++ ")"++instance (ArbitraryPartial a, ArbitraryPartial b) => ArbitraryPartial (a,b) where+ arbitraryPartial = liftM2 (,) arbitraryPartial arbitraryPartial++instance (Show (Partial a), Show (Partial b), Show (Partial c)) => Show (Partial (a,b,c)) where+ show = showPartial "(,)" showTriple+ where showTriple (Partial (a,b,c)) = + "(" ++ show (Partial a) ++ "," + ++ show (Partial b) ++ "," + ++ show (Partial c) ++ ")"++instance (ArbitraryPartial a, ArbitraryPartial b, ArbitraryPartial c) => ArbitraryPartial (a,b,c) where+ arbitraryPartial = liftM3 (,,) arbitraryPartial arbitraryPartial arbitraryPartial
+ src/Test/ClassLaws/TestingDatatypes.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}++-- | Some example usage of the ClassLaws framework+module Test.ClassLaws.TestingDatatypes where+import Control.Monad.State (State, runState, state, liftM)+import Data.Monoid (Endo(Endo))+import Test.ClassLaws++-- | To check equality of 'Endo'-functions we can generate argument values.+type instance Param (Endo a) = a++instance (SemanticEq (Endo a), Show (Partial (Endo a))) => TestEqual (Endo a) where+ testEqual l _ = testEqPartial (==!) l++{-+instance (Eq ( a), Show (Partial a)) => TestEqual (Endo a) where+ testEqual = testRunEqPartial appEndo (==)+-}++instance (Arbitrary a, CoArbitrary a) => Arbitrary (Endo a) where+ arbitrary = liftM Endo arbitrary++-- | For lists, no 'Param'eter is needed, so we use @()@.+type instance Param [a] = ()++instance (Eq a, Show a) => TestEqual [a] where+ testEqual p _ = testEq (==) p++-- Maybe+type instance Param (Maybe a) = ()++instance (Eq a, Show a) => TestEqual (Maybe a) where+ testEqual p _ = testEq (==) p++-- State+type instance Param (State s a) = s++instance (Eq a, Show a, Eq s, Show s) => TestEqual (State s a) where+ testEqual = testRunEq runState (==) ++instance (CoArbitrary s, Arbitrary a, Arbitrary s) => Arbitrary (State s a) where+ arbitrary = fmap state arbitrary ++-- | We use the MyList datatype to provide instances that do not+-- satisfy some class laws.+data MyList a = Cons a (MyList a) + | Nil + deriving (Show, Eq)++foldrMyList :: (a -> b -> b) -> b -> MyList a -> b+foldrMyList f e Nil = e+foldrMyList f e (Cons x xs) = f x (foldrMyList f e xs)++list2MyList :: [a] -> MyList a +list2MyList [] = Nil+list2MyList (x:xs) = Cons x (list2MyList xs)++myList2List :: MyList a -> [a]+myList2List Nil = []+myList2List (Cons x xs) = x:myList2List xs++(+++) :: MyList a -> MyList a -> MyList a+Nil +++ xs = xs+(Cons y ys) +++ xs = Cons y (ys +++ xs)++snoc :: a -> MyList a -> MyList a+snoc y Nil = Cons y Nil+snoc y (Cons x xs) = Cons x (snoc y xs)++instance Arbitrary a => Arbitrary (MyList a) where+ arbitrary = fmap list2MyList arbitrary+ shrink = map list2MyList . shrink . myList2List++type instance Param (MyList a) = ()++instance (Eq a, Show a) => TestEqual (MyList a) where+ testEqual p _ = testEq (==) p
+ src/Test/ClassLaws/TestingEquality.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE FlexibleContexts #-}++{- |++The following class + helper functions implement law-agnostic testing+functionality that is used to test laws for various classes.++-}++module Test.ClassLaws.TestingEquality where+import Test.QuickCheck.Property + +import Test.ClassLaws.Core(Equal, Param)+import Test.ClassLaws.Partial(Partial(Partial))++-- | A class for types which can be checked for 'Equal'ity, possibly+-- needing some extra 'Param'eters.+class TestEqual b where+ testEqual :: Equal b -> Param b -> Property++-- | The first function, 'testRunEq', returns a property implementing+-- an equality check. It takes a function that can `run' a value and a+-- comparison operator to a predicate (which in turn takes some+-- supposedly equal values, and a parameter needed for the run+-- function, and returns a 'Property').+testRunEq :: Show r => (t -> p -> r) -> (r -> r -> Bool) -> + (Equal t -> p -> Property)+testRunEq run (==) steps p = testEq (==) (map (`run` p) steps)++-- | The second function, 'testEq', does the same, but now for pairs+-- that are not necessarily runnable.+testEq :: Show a => (a -> a -> Bool) -> + (Equal a -> Property)+testEq (==) steps = + whenFail (print $ failingPair (==) steps)+ $ property $ liftBool $ pairwiseEq (==) steps++----++-- | Variant of 'testRunEq' intended for 'Partial' values. (Only the+-- Show part differs - the user also needs to supply an equality+-- operator handling 'Partial' values.)+testRunEqPartial :: Show (Partial r) => + (t -> p -> r) -> (r -> r -> Bool) -> + (Equal t -> p -> Property)+testRunEqPartial run (==) steps p = testEqPartial (==) (map (`run` p) steps)++-- | Similar variant of 'testEq' for 'Partial' values.+testEqPartial :: Show (Partial a) => (a -> a -> Bool) -> Equal a -> Property+testEqPartial (==) steps = + whenFail (print $ Partial (failingPair (==) steps)) + $ property $ liftBool (pairwiseEq (==) steps)++----++-- | Local helper+pairwiseEq :: (r -> r -> Bool) -> [r] -> Bool+pairwiseEq (==) [] = True+pairwiseEq (==) [x] = True+pairwiseEq (==) (x:y:ys) = x==y && pairwiseEq (==) (y:ys)++-- | Position in an equality proof+type Pos = Int++-- | Local helper+failingPair :: (a -> a -> Bool) -> Equal a -> (Pos, a, a)+failingPair = failingPair' 1+-- | Local helper+failingPair' pos (==) (x:y:ys) = if not (x==y) + then (pos,x,y) + else failingPair' (1+pos) (==) (y:ys)++{- The following function generalises testEq and testRunEq+testRunEq :: Show r =>+ Maybe (p,r -> p -> r) -> (r -> r -> Bool) -> Equal r -> Property+testRunEq startrun (==) steps = + let run = case startrun of + Nothing -> id+ Just (start,run) -> flip run start+ in whenFail (print (failingPair (==) (map run steps)))+ $ property + $ liftBool (pairwiseEq (==) (map run steps))++-- An instance of testRunEq+testEq :: Show a => (a -> a -> Bool) -> Equal a -> Property+testEq = testRunEq Nothing+-}
+ src/Test/ClassLaws/TestingFinFuns.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverlappingInstances #-}++-- | Functions from a finite type can be shown, checked for equality,+-- and generated. We provide variants both for total and for partial+-- values.+module Test.ClassLaws.TestingFinFuns where+import Test.QuickCheck+import Test.ClassLaws.Partial+ (Partial(..), ArbitraryPartial(arbitraryPartial), genPartial+ , bottom, isBottom+ , SemanticEq ( (==!), semanticEq ), Tweak+ , SemanticOrd ( (<=!), semanticCompare, (/\!), semanticMeet, semanticJoin )+ )+import Data.List (intersperse)+import Control.Monad (forM)++showPartialFun ::+ (Bounded a, Enum a, Show (Partial b), Show (Partial a)) =>+ (a -> b) -> String+showPartialFun f = + if isBottom f + then "<_bot_a->b_>"+ else "<(" +++ (concat $ intersperse "; "+ [ show (Partial x) ++ "->" ++ + show (Partial (f x))+ | x <- (bottom:enumElems)])+ ++ ")>"+++showFun :: (Enum a, Bounded a, Show a, Show b) => (a -> b) -> String+showFun f = "<(" ++ (concat $ intersperse "; "+ [ show x ++ "->" ++ show (f x)+ | x <- enumElems])+ ++ ")>"++enumElems :: (Bounded a, Enum a) => [a]+enumElems = [minBound .. maxBound]+++arbitraryPartialFun :: forall e a. + (Enum e, Bounded e, SemanticOrd a) => + Gen a -> Gen (e -> a)+arbitraryPartialFun ag = do+ funtab <- forM (bottom : enumElems :: [e]) (\_ -> ag)+ genPartial 1 10 (return (table2fun funtab))++type FunTab e a = [a]++table2fun :: (Enum e, Bounded e, SemanticOrd a) => + FunTab e a -> (e -> a)+table2fun tab@(_:tottab) = fun+ where meet = lMeet tab+ fun x | isBottom x = meet + | otherwise = tail tottab !! (fromEnum x)++lMeet :: (SemanticOrd a) => [a] -> a+lMeet [] = bottom+lMeet [x] = x+lMeet (x:xs) = x /\! lMeet xs++------------------------------------------------------------++instance (Enum a, Bounded a, Show a, Show b) =>+ Show (a->b) where+ show = showFun++instance ( Enum e, Bounded e, Eq e + , SemanticOrd s, ArbitraryPartial s + ) => ArbitraryPartial (e -> s) where + arbitraryPartial = arbitraryPartialFun arbitraryPartial++instance ( Enum e, Bounded e+ , Show (Partial e), Show (Partial b)+ ) => Show (Partial (e->b)) where+ show (Partial f) = showPartialFun f+------------------------------------------------------------++semanticLE _ a b = case ( isBottom a, isBottom b ) of+ (True, _) -> True+ _ -> False+------------------------------------------------------------++instance (Bounded a, Enum a, Eq b) => Eq (a->b) where+ f == g = all (\x -> f x == g x) enumElems++instance (Bounded a, Enum a, SemanticEq b) => SemanticEq (a->b) where+ semanticEq = semEqFun semanticEq+ +type SemEq a = Tweak->a->a->Bool+semEqFun :: (Bounded a, Enum a) => SemEq b -> SemEq (a->b)+semEqFun semEqB tweak f g = eqPartial (all (\x -> semEqB tweak (f x) (g x)) + (bottom : enumElems)) + f g++instance (Bounded a, Enum a, SemanticOrd b) => SemanticOrd (a->b) where+ semanticCompare tweak f g =+ case ( semanticEq tweak f g+ , isBottom f+ , isBottom g ) of+ (True, _, _) -> Just EQ+ (_, True, _) -> Just LT+ (_, _, True) -> Just GT+ (_, _, _) -> + if lessEqPartial (all (\x -> f x <=! g x) enumElems) f g then+ Just LT+ else if lessEqPartial (all (\x -> g x <=! f x) enumElems) f g then+ Just GT+ else+ Nothing+ semanticJoin tweak f g = undefined+ -- semanticJoin tweak f g = case (isBottom f, isBottom g) of+ -- (True, True) -> Just bottom+ -- (True, False) -> Just g+ -- (False, True) -> Just f+ -- (False, False) -> (\x -> (\/!) (f x) (g x))+ -- propagate Nothing here how?+ semanticMeet tweak f g = case (isBottom f, isBottom g) of+ (False, False) -> \x -> (/\!) (f x) (g x)+ (_, _) -> bottom++------------------------------------------------------------+++lessEqPartial nonBotLE x y = case (isBottom x, isBottom y) of+ (True, _) -> True+ (False, True) -> False + (False, False) -> nonBotLE++eqPartial nonBotEq x y = case (isBottom x, isBottom y) of+ (True, True) -> True+ (False, False) -> nonBotEq+ _ -> False ++meetPartial q x y = case (isBottom x, isBottom y) of+ (False, False) -> q+ _ -> bottom+
+ src/Test/ClassLaws/TestingState.hs view
@@ -0,0 +1,276 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++-- | Implementations of the infrastructure needed to test state monad+-- laws.+module Test.ClassLaws.TestingState where+import Test.ClassLaws+{-+-- More details about the imports.+import Test.QuickCheck+import Test.ChasingBottoms+ ( bottom, isBottom+ , SemanticEq ( (==!), semanticEq )+ , SemanticOrd ( semanticCompare, semanticMeet, semanticJoin )+ )+import Test.ClassLaws.Partial+ (Partial(..), ArbitraryPartial(arbitraryPartial), genPartial)+import Test.ClassLaws.TestingEquality+ ( TestEqual(testEqual)+ , testEqPartial, testEq+ , testRunEqPartial, testRunEq)+-}++import Test.ClassLaws.TestingFinFuns++import Control.Monad.State.Class(MonadState(..))+import Control.Monad (liftM, liftM2)++import Data.Data++data Pair a b = Pair a b++fstP ~(Pair a b) = a+sndP ~(Pair a b) = b++newtype State s a = S {runS :: s -> Pair a s}++getState :: State s s+getState = S $ \s -> Pair s s++putState :: s -> State s ()+putState s = S $ const (Pair () s)++returnState a = S $ \s -> Pair a s+++bindStateL m k = S $ \s -> let (Pair a s') = runS m s+ in runS (k a) s'++fmapStateL f m = S $ \s -> let (Pair a s') = runS m s+ in (Pair (f a) s')+++bindStateS m k = S $ \s -> case runS m s of+ (Pair a s') -> runS (k a) s'++fmapStateS f m = S $ \s -> case runS m s of+ (Pair a s') -> (Pair (f a) s')++------------------------------------------------------------++pairFromGen :: Gen a -> Gen b -> Gen (Pair a b)+pairFromGen a b = (liftM2 Pair a b)++pairShowPartial :: String -> Pair a b -> String+pairShowPartial _ p | isBottom p = "_|_P_"+pairShowPartial pab (Pair a b) = pab++basicPairShow :: (a-> String) -> (b -> String) -> Pair a b -> String+basicPairShow sa sb (Pair a b) = "("++sa a++", "++sb b++")"++instance (Arbitrary a, Arbitrary b) => Arbitrary (Pair a b) where+ arbitrary = pairFromGen arbitrary arbitrary++instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Pair a b) where+ coarbitrary (Pair a b) = variant 1 . coarbitrary a . coarbitrary b++instance (Show a, Show b) => Show (Pair a b) where+ show = basicPairShow show show++instance (ArbitraryPartial a, ArbitraryPartial b) =>+ ArbitraryPartial (Pair a b) where+ arbitraryPartial = genPartial 1 9 $ pairFromGen arbitraryPartial arbitraryPartial++instance (Show (Partial a), Show (Partial b)) =>+ Show (Partial (Pair a b)) where+ show (Partial p) = pairShowPartial (basicPairShow (show.Partial) (show.Partial) p) p++------------------------------------------------------------++instance ( Arbitrary a+ , Arbitrary s+ , CoArbitrary s+ ) => Arbitrary (State s a) where+ arbitrary = liftM S arbitrary++instance (Enum s, Bounded s, Show a, Show s) =>+ Show (State s a) where+ show (S f) = "(S " ++ show f ++ ")"++instance ( ArbitraryPartial a, SemanticOrd a+ , ArbitraryPartial s, SemanticOrd s + , Enum s, Bounded s, Eq s + ) => ArbitraryPartial (State s a) where+ arbitraryPartial = genPartial 1 20 (liftM S arbitraryPartial)++instance (Enum s, Bounded s, Show (Partial a), Show (Partial s)) =>+ Show (Partial (State s a)) where+ show (Partial s) | isBottom s = "_|_St_"+ show (Partial (S f)) = "(S " ++ show (Partial f) ++ ")"++------------------------------------------------------------++instance (Eq a, Eq b) => Eq (Pair a b) where+ px == py = pairRecPatt (==) (==) (&&) px py++instance (SemanticEq a, SemanticEq b) => SemanticEq (Pair a b) where+ semanticEq tweak x y =+ -- case ( isBottomTimeOut (timeOutLimit tweak) x+ -- , isBottomTimeOut (timeOutLimit tweak) y ) of+ case ( isBottom x, isBottom y ) of+ (True, True) -> True+ (False, False) ->+ ((semanticEq tweak) (fstP x) (fstP y)) &&+ ((semanticEq tweak) (sndP x) (sndP y))+ _ -> False++instance (SemanticOrd a, SemanticOrd b) => SemanticOrd (Pair a b) where+ semanticCompare tweak x y =+ case ( semanticEq tweak x y+ , isBottom x+ , isBottom y ) of+ (True, _, _) -> Just EQ+ (_, True, _) -> Just LT+ (_, _, True) -> Just GT+ (_, _, _) -> + case (l == r) of+ True -> l+ _ -> Nothing+ where+ l = semanticCompare tweak (fstP x) (fstP y)+ r = semanticCompare tweak (sndP x) (sndP y)+ semanticJoin tweak x y = case (isBottom x, isBottom y) of+ (True, True) -> Just bottom+ (True, False) -> Just y+ (False, True) -> Just x+ -- (False, True) -> cast x+ (False, False) -> case ( semanticJoin tweak (fstP x) (fstP y)+ , semanticJoin tweak (sndP x) (sndP y)) of+ (Nothing, _) -> Nothing+ (_, Nothing) -> Nothing+ (Just fst, Just snd) -> Just $ Pair fst snd+ semanticMeet tweak x y = case (isBottom x, isBottom y) of+ (True, _) -> bottom+ (_, True) -> bottom+ (False, False) -> Pair (semanticMeet tweak (fstP x) (fstP y))+ (semanticMeet tweak (sndP x) (sndP y))++-- -- semanticLE _tweak a b = case ( isBottomTimeOut (timeOutLimit tweak) a+-- -- , isBottomTimeOut (timeOutLimit tweak) b ) of+-- semanticLE _ a b = case ( isBottom a, isBottom b ) of+-- (True, _) -> True+-- _ -> False++pairRecPatt :: (a->a->ta) -> (b->b->tb) -> (ta->tb->t) -> Pair a b -> Pair a b -> t+pairRecPatt opA opB topOp px py = topOp (opA (fstP px) (fstP py)) (opB (sndP px) (sndP py))++------------------------------------------------------------++instance (Enum a, Bounded a, Eq a, Eq b) => Eq (State a b) where+ (==) x y = eqPartial q x y+ where q = statePatt (==) x y+ +instance (Enum a, Bounded a, SemanticEq a, SemanticEq b) => SemanticEq (State a b) where+ semanticEq tweak x y = eqPartial q x y+ where q = statePatt (semanticEq tweak) x y+instance (Enum a, Bounded a, SemanticOrd a, SemanticOrd b) => SemanticOrd (State a b) where+ semanticCompare tweak x y =+ case ( semanticEq tweak x y+ , isBottom x+ , isBottom y ) of+ (True, _, _) -> Just EQ+ (_, True, _) -> Just LT+ (_, _, True) -> Just GT+ (_, _, _) -> statePatt (semanticCompare tweak) x y+ semanticJoin tweak x y = error "TODO: semanticJoin for State not yet implemented"+ -- semanticJoin tweak f g = case (isBottom f, isBottom g) of+ -- (True, True) -> Just bottom+ -- (True, False) -> Just g+ -- (False, True) -> Just f+ -- (False, False) -> (\x -> (\/!) (f x) (g x))+ -- propagate Nothing here how?+ semanticMeet tweak x y = case (isBottom x, isBottom y) of+ (False, False) -> S $ statePatt (semanticMeet tweak) x y+ (_, _) -> bottom++statePatt op (S f1) (S f2) = op f1 f2++------------------------------------------------------------++instance Arbitrary Ordering where+ arbitrary = enumTotArb $ zip [1,1,1] $ enumElems++instance CoArbitrary Ordering where+ coarbitrary = coarbitrary . fromEnum++instance ArbitraryPartial Ordering where+ arbitraryPartial = genPartial 1 9 $ enumTotArb $+ zip [1,1,1] $ enumElems++instance Show (Partial Ordering) where+ show = enumShowBot_auxLst ["Ord", "LT", "EQ", "GT"] . unPartial++enumTotArb :: [(Int,a)] -> Gen a+enumTotArb as = frequency $ map (\(f,a) -> (f,return a)) as++enumShowBot_auxLst :: (Bounded a, Enum a) => [String] -> a -> String+enumShowBot_auxLst (s:ss) x | isBottom x = "_|_"++s++"_"+enumShowBot_auxLst (s:ss) x = ss !! fromEnum x++++++-- instances for the lazy state+instance Monad (State s) where+ return = returnState+ (>>=) = bindStateL++instance Functor (State s) where+ fmap = fmapStateL++instance Monad (State s) => MonadState s (State s) where+ get = getState+ put = putState+++instance ( SemanticEq a, Show (Partial a)+ , SemanticEq s, Show (Partial s)+ , Bounded s, Enum s) => TestEqual (State s a) where+ testEqual eq _ = testEqPartial (==!) (map runS eq)++type instance Param (State s a) = s++----------------------------------------------------------------------+newtype SS s a = SS {unSS :: State s a}+ deriving( Arbitrary, Show, ArbitraryPartial, MonadState s)++instance ( SemanticEq a, Show (Partial a)+ , SemanticEq s, Show (Partial s)+ , Bounded s, Enum s) => TestEqual (SS s a) where+ testEqual eq _ = testEqPartial (==!) (map unSS eq)+instance (Enum s, Bounded s, Show (Partial a), Show (Partial s)) =>+ Show (Partial (SS s a)) where+ show (Partial (SS x)) = show (Partial x)++++instance Monad (SS s) where+ return = SS . returnState+ (SS m) >>= k = bindSS + where+ bindSS = SS $ S $ \s -> case runS m s of+ (Pair a s') -> x s'+ where+ SS (S x) = k a++instance Functor (SS s) where+ fmap f (SS m) = SS $ S $ \s -> case runS m s of+ (Pair a s') -> (Pair (f a) s')++type instance Param (SS s a) = s