如何修复类似列表类型的任意实例的歧义

时间:2018-01-04 12:00:09

标签: haskell quickcheck

请考虑以下事项:

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

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

instance Functor List where
    fmap _ Nil = Nil
    fmap f (Cons a l) = Cons (f a) (fmap f l)

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

genList :: Arbitrary a => Gen (List a)
genList = do
    n <- choose (3 :: Int, 5)
    gen <- arbitrary
    elems <- vectorOf n gen
    return $ build elems
  where build [] = Nil
        build (e:es) = Cons e (build es)

instance Arbitrary a => Arbitrary (List a) where
    arbitrary = frequency [ (1, return Nil)
                          , (3, genList)
                          ]

main = quickBatch $ functor (Nil :: List (Int, String, Int))

由于genList

中含糊不清,因此无法编译
• Could not deduce (Arbitrary (Gen a))
    arising from a use of ‘arbitrary’
  from the context: Arbitrary a
    bound by the type signature for:
               genList :: forall a. Arbitrary a => Gen (List a)
    at reproducer.hs:13:1-38
• In a stmt of a 'do' block: gen <- arbitrary
  In the expression:
    do n <- choose (3 :: Int, 5)
       gen <- arbitrary
       elems <- vectorOf n gen
       return $ build elems
  In an equation for ‘genList’:
      genList
        = do n <- choose (3 :: Int, 5)
             gen <- arbitrary
             elems <- vectorOf n gen
             ....
        where
            build [] = Nil
            build (e : es) = Cons e (build es)
   |
16 |     gen <- arbitrary
   |            ^^^^^^^^^

我知道我可以将genList写为genList = Cons <$> arbitrary <*> arbitrary,但我想知道。如何消除歧义? main中的类型断言是不是应该清除它?

1 个答案:

答案 0 :(得分:3)

vectorOf需要Gen a。因此,GHC尝试为Arbitrary找到Gen a的实例,该实例不存在。

使用vectorOf n arbitraryvector n。此外,建议使用sized让QuickCheck选择大小:

genList :: Arbitrary a => Gen (List a)
genList = sized $ \n ->
    elems <- vector n
    return $ build elems
  where build [] = Nil
        build (e:es) = Cons e (build es)

但是,在那时我们已经更好地使用listOf了:

genList :: Arbitrary a => Gen (List a)
genList = build <$> listOf arbitary
  where build = foldr Cons Nil

请注意,genList = Cons <$> arbitrary <*> arbitrary不会生成空列表,而foldr Cons Nil <$> listOf arbitrary会生成空列表。