Search code examples
haskellquickcheck

Haskell arbitrary typeclass instance return type


From the Haskell Book chapter on Monoids, I am writing quickcheck tests for

semigroupAssoc :: (Eq m, S.Semigroup m) => m -> m -> m -> Bool
semigroupAssoc a b c =
    (a S.<> (b S.<> c)) == ((a S.<> b) S.<> c)

type IdentAssoc = Identity String -> Identity String -> Identity String -> Bool

, invoking with

quickCheck (semigroupAssoc :: IdentAssoc)

Here is the arbitrary typeclass instance

instance (Arbitrary a) => Arbitrary (Identity a) where
    arbitrary = do
        a <- Test.QuickCheck.arbitrary
        return (Identity a)

My question is that in my arbitrary instance, I can return either (Identity a) or just a. (Identity a) is correct, but just a gives no compiler error and causes an infinite loop when run. Why is this?


Solution

  • Type inference changes the arbitrary call to point at the same instance we are defining right now, causing an infinite loop.

    instance (Arbitrary a) => Arbitrary (Identity a) where
       arbitrary = do
            x <- Test.QuickCheck.arbitrary  -- this calls arbitrary @ a
            return (Identity x)
    
    instance (Arbitrary a) => Arbitrary (Identity a) where
       arbitrary = do
            x <- Test.QuickCheck.arbitrary  -- this calls arbitrary @ (Identity a)
            return x
    

    Every time we call a class method, the compiler infers which instance to call.

    In the former case, the compiler infers x :: a (only this type makes the code type check!), hence it calls arbitrary @ a.

    In the latter case, the compiler infers x :: Identity a (only this type makes the code type check!), hence it calls arbitrary @ (Identity a), causing the infinite loop.