为什么此列表的Traversable实例不正确?

时间:2016-03-21 11:05:34

标签: haskell traversable

以下代码无法通过checkers测试遍历。我很欣赏它失败的原因,而不仅仅是如何修复它。

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)

您在此处看到它通过了fmap法律,但未通过foldMap法律:

λ> 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 个答案:

答案 0 :(得分:6)

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

您的可折叠实例不会遍历列表的尾部。

checkers通过一起测试TraversableFunctor来测试您的Foldable个实例:它从{的实现foldMapfmap中获取{ {1}}并确保它们与您定义的TraversablefoldMap产生相同的结果。