3
votes

I have this type which wraps a function

newtype Transaction d e r = Transaction (d -> Either e (d,r))

...and I want to do quickcheck tests for its Functor & Applicative instances but the compiler complains that it doesn't have an Arbitrary instance.

I tried to do so but I'm stuck at generating the random function.

Thanks!

== UPDATE ==

The quickcheck properties are defined like this

type IdProperty f a = f a -> Bool
functorIdProp :: (Functor f, Eq (f a)) => IdProperty f a
functorIdProp x = (fmap id x) == id x

type CompositionProperty f a b c = f a -> Fun a b -> Fun b c -> Bool
functorCompProp :: (Functor f, Eq (f c)) => CompositionProperty f a b c
functorCompProp x (apply -> f) (apply -> g) = (fmap (g . f) x) == (fmap g . fmap f $ x)

instance (Arbitrary ((->) d  (Either e (d, a)))) => Arbitrary (DbTr d e a) where
    arbitrary = do
      f <- ...???
      return $ Transaction f

...and the tests look like this:

spec = do
  describe "Functor properties for (Transaction Int String)" $ do
    it "IdProperty (Transaction Int String) Int" $ do
      property (functorIdProp :: IdProperty (Transaction Int String) Int)

    it "CompositionProperty (Transaction Int String) Int String Float" $ do
      property (functorCompProp :: CompositionProperty (Transaction Int String) Int String Float)
1
Maybe this will be of use? I'd think about what are you really going to gain from such random functions, though. I'd say that some edge cases is everything that this needs.Bartek Banachewicz

1 Answers

3
votes

You should use Test.QuickCheck.Function wrapper to test functions. It seems to have no sense in having Arbitrary or CoArbitrary instances for Transaction if you only need to test type class laws for your Transaction type (but if you really need it, you can find my answer to this question).

To test laws you can write properties in the way like this:

{-# LANGUAGE DeriveFunctor #-}

import Test.QuickCheck
import Test.QuickCheck.Function

newtype Transaction d e r = Transaction (d -> Either e (d,r))
  deriving (Functor)

-- fmap id ≡ id
prop_transactionFunctorId :: Int
                          -> Fun Int (Either Bool (Int,String)) 
                          -> Bool
prop_transactionFunctorId d (Fun _ f) = let t              = Transaction f
                                            Transaction f' = fmap id t
                                        in f' d == f d

Well, this may look like not as pretty and nice as you want. But this is a good way to test arbitrary functions. For example, we can replace last line in f' d == f d to in f' d == f 1 to see how it fails if it fails:

ghci> quickCheck prop_transactionFunctorId 
*** Failed! Falsifiable (after 2 tests and 4 shrinks):    
0
{0->Left False, _->Right (0,"")}