tesserakt
tesserakt

Reputation: 3331

Checkers and EqProp

I am going through the Haskell Book and the chapter on applicatives started using the checkers library for testing.

This library introduces the EqProp typeclass, and I don't understand how it differs from Eq. Most of the examples that call for EqProp use Eq under the hood anyway.

Why do people use checkers over QuickCheck?

Upvotes: 3

Views: 436

Answers (2)

duplode
duplode

Reputation: 34378

This library introduces the EqProp typeclass, and I don't understand how this differs from Eq.

(==) from Eq gives you a Bool; (=-=) from EqProp gives you a QuickCheck Property. If you have an instance of Eq, you can also make it an EqProp instance, as witnessed by eq. The interesting instances of EqProp are those that aren't instances of Eq, such as functions (cf. Li-yao Xia's answer).

Why do people use checkers over Quickcheck?

Checkers is a set of extra tools built upon QuickCheck. As its readme and Hackage description suggest, one of its goals is making it easy to express class laws as Quickcheck properties. For instance, note how the definition of the applicative "test batch", which I will quote below, looks a lot like how we often write the Applicative laws, and is nicely self-documenting.

(Feel free to skip the bookkeeping constraints in the top-level signature. Also note that the m (a,b,c) argument is, in a simplest-thing-that-could-possibly-work fashion, only used for type specialisation, the intent being that you pass e.g. undefined :: [(Int,Int,Int)] to test the list instance of Applicative.)

-- | Properties to check that the 'Applicative' @m@ satisfies the applicative
-- properties
applicative :: forall m a b c.
               ( Applicative m
               , Arbitrary a, CoArbitrary a, Arbitrary b, Arbitrary (m a)
               , Arbitrary (m (b -> c)), Show (m (b -> c))
               , Arbitrary (m (a -> b)), Show (m (a -> b))
               , Show a, Show (m a)
               , EqProp (m a), EqProp (m b), EqProp (m c)
               ) =>
               m (a,b,c) -> TestBatch
applicative = const ( "applicative"
                    , [ ("identity"    , property identityP)
                      , ("composition" , property compositionP)
                      , ("homomorphism", property homomorphismP)
                      , ("interchange" , property interchangeP)
                      , ("functor"     , property functorP)
                      ]
                    )
 where
   identityP     :: m a -> Property
   compositionP  :: m (b -> c) -> m (a -> b) -> m a -> Property
   homomorphismP :: (a -> b) -> a -> Property
   interchangeP  :: m (a -> b) -> a -> Property
   functorP      :: (a -> b) -> m a -> Property

   identityP v        = (pure id <*> v) =-= v
   compositionP u v w = (pure (.) <*> u <*> v <*> w) =-= (u <*> (v <*> w))
   homomorphismP f x  = (pure f <*> pure x) =-= (pure (f x) :: m b)
   interchangeP u y   = (u <*> pure y) =-= (pure ($ y) <*> u)
   functorP f x       = (fmap f x) =-= (pure f <*> x)

Upvotes: 3

Li-yao Xia
Li-yao Xia

Reputation: 33464

The point is that functions and infinite values do not have decidable equality (Eq), but can be tested for it (EqProp) by observing a (random) finite fragment.

Upvotes: 2

Related Questions