创建一个有条件的任意实例(*由于使用“quickCheck”而产生的不明确类型变量“a”)

Creating a conditioned Arbitrary instance ( * Ambiguous type variable `a' arising from a use of `quickCheck')

我想做这个测试:

prop_inverse_stringsToInts st = isDigitList st ==> st == map show (stringsToInts st)

它正在测试一个将字符串列表转换为整数列表的函数,但当然字符串需要是数字,所以我创建了一个先决条件来使用我创建的 isDigitList 函数进行检查,但是条件太具体,quickCheck 放弃:“*** 放弃!仅通过 43 项测试;1000 项测试被丢弃。” 所以我想为我的案例创建一个 Arbitrary 实例,但问题是我没有使用 Arbitrary 的经验,所以我真的不知道该怎么做,每次我打乱代码时我都会收到一个新错误。我想要的只是一个 Arbitrary,只有 returns Foo [String] 如果它通过 isDigitList(它接收一个 [String] 和 returns 一个 Bool)。到目前为止,我有这样的东西:

  Foo a = Foo [String] deriving (Show,Eq)

  instance (Arbitrary a) => Arbitrary (Foo a ) where
           arbitrary = do 
                                st <- (arbitrary :: Gen [String])
                                if isDigitList st 
                                then do return (Foo st) 
                                else do return (Foo []) -- This is probably a bad idea

我将 属性 更改为:

prop_inverse_stringsToInts :: Foo a -> Bool
prop_inverse_stringsToInts (Foo st) =  st == map show (stringsToInts st)

但现在我收到错误“* Ambiguous type variable a0' arising from a use of `quickCheck'”,即使我是这样 运行 quickCheck : > quickCheck (prop_inverse_stringsToInts : : Foo a -> Bool)

有人可以帮忙吗?提前致谢!

看来您了解基础知识,但为了确保万无一失,我会在这里重复一遍。有两种方法可以让 QuickCheck 生成您想要的输入:

  1. 让它生成一些输入,然后过滤掉不需要的,或者

  2. 让它只生成您想要的输入。

您从选项 1 开始,但如您所见,结果并不理想。与 String 的所有可能列表相比,数字列表确实没有那么多。更好的选择是只生成你想要的输入。

要在选项 2 上取得成功,您需要制作一个 生成器 ,它是一个 Gen [String] 类型的值,它生成 String 的列表符合您的标准。您建议的生成器仍然使用过滤的方法,因此您可能想尝试不同的方法。相反,请考虑以下内容:

genDigitStrings :: Gen [String]
genDigitStrings = do
  intList <- arbitrary :: Gen [Integer]
  return $ fmap show intList

此生成器生成任意 String 列表,这些列表始终显示为整数,这意味着它们将始终是数字列表。如果需要,您可以继续将其插入到某个新类型的 Arbitrary 实例中。

为了您自己的理智,您甚至可以通过这样的测试来检查您的工作:

propReallyActuallyDigitStrings = forAll genDigitStrings isDigitList

如果通过,则您有信心您的生成器实际上只生成数字列表,如果失败,则您应该调整您的生成器。