Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this instance of Traversable for list not correct?

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))))
like image 773
The Unfun Cat Avatar asked Mar 21 '16 11:03

The Unfun Cat


1 Answers

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

Your Foldable instance doesn't traverse the tail of the list.


checkers tests your Traversable instances by testing Functor and Foldable all together: it derives foldMap and fmap from your implementation of Traversable and make sure they produce the same result as foldMap and fmap you defined.

like image 192
zakyggaps Avatar answered Sep 27 '22 19:09

zakyggaps