Why is this instance of Traversable for list not correct?

Viewed 460

The below code fails the checkers test for traversable. I'd appreciate an explanation of why it fails, not just how to fix it.

import Test.QuickCheck
import Test.QuickCheck.Checkers
import Test.QuickCheck.Classes

data List a =
  Nil
  | Cons a (List a)
    deriving (Show, Eq, Ord)

instance Functor List where
  fmap _ Nil = Nil
  fmap f (Cons x xs) = (Cons (f x) (fmap f xs))

instance Foldable List where
  foldr f z (Cons x xs) = f x z
  foldr _ z Nil = z

instance Traversable List where
  traverse f Nil = pure Nil
  -- traverse f (Cons x Nil) = Cons <$> f x <*> pure Nil
  traverse f (Cons x xs) = Cons <$> f x <*> (traverse f xs)

instance Arbitrary a => Arbitrary (List a) where
  arbitrary = sized go
    where go 0 = pure Nil
          go n = do
            xs <- go (n - 1)
            x <- arbitrary
            return (Cons x xs)

type TI = List

instance Eq a => EqProp (List a) where (=-=) = eq

main = do
  let trigger = undefined :: TI (Int, Int, [Int])
  -- quickBatch (functor trigger)
  quickBatch (traversable trigger)

Here you see that it passes the fmap laws, but not the foldMap ones:

λ> main

traversable:
  fmap:    +++ OK, passed 500 tests.
  foldMap: *** Failed! Falsifiable (after 6 tests): 
<function>
Cons 4 (Cons (-2) (Cons (-5) (Cons 5 (Cons 2 Nil))))
1 Answers
Related