为什么 quickcheck 通过这两个不同的函数 Haskell?

Why does quickcheck pass for these two different functions Haskell?

我有两个功能。他们是:

f1 [] = []
f1 (x:xs) = if contains x xs then f1 xs else x:f1 xs

f6 xs = f1 (rev xs)

这两个函数 return 除了空列表和任何具有一个元素的列表之外的任何列表都是相同的列表,但是当 运行 快速检查此函数时:

prop_sort6 xs = (f1 xs == f6 xs) == True

所有测试都通过了。为什么会这样?

编辑:

例如这样做:(f1 [1,2,3] == f6 [1, 2, 3]) 显然会导致 False,但 quickcheck 仍然通过。

指定您的函数应该在例如整数列表:

prop_sort6 :: [Int] -> Bool
prop_sort6 xs = (f1 xs == f6 xs) == True

如果您不指定类型,QuickCheck 会将 [a] 填写为 [()],并且 [(), (), (), (), (), ...] 不会在您的案例中发现任何错误。

我们可以使用 verboseCheck 而不是 quickCheck 来做一些调查。

*Main Test.QuickCheck> verboseCheck prop_sort6
Passed:
[]

Passed:
[]

Passed:
[(),()]

Passed:
[(),()]

Passed:
[(),(),(),()]

... (you get the picture) ...

quickCheck(和verboseCheck,同理)有签名

quickCheck :: Testable prop => prop -> IO ()

现在,我们可以顺着兔子洞往下看,看看 Testable 是什么,但最重要的是,无论 prop 是什么,它都必须在 [= 处是单态的61=]时间。也就是说,它不能有任何讨厌的挥之不去的类型变量。现在,prop_sort6 的类型被推断为

prop_sort6 :: Eq a => [a] -> Bool

所以我们需要一个好的混凝土a满足Eq。对于大多数类型类,这将是一个不明确的类型错误。如果我们这样写,

class Foo a

myProp :: Foo a => a -> Bool
myProp _ = True

然后quickCheck myProp产生

<interactive>:29:1: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘quickCheck’
      prevents the constraint ‘(Arbitrary a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instances exist:
        instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b)
          -- Defined in ‘Test.QuickCheck.Arbitrary’
        instance Arbitrary Ordering
          -- Defined in ‘Test.QuickCheck.Arbitrary’
        instance Arbitrary Integer
          -- Defined in ‘Test.QuickCheck.Arbitrary’
        ...plus 19 others
        ...plus 61 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: quickCheck myProp
      In an equation for ‘it’: it = quickCheck myProp

<interactive>:29:12: error:
    • No instance for (Foo a0) arising from a use of ‘myProp’
    • In the first argument of ‘quickCheck’, namely ‘myProp’
      In the expression: quickCheck myProp
      In an equation for ‘it’: it = quickCheck myProp

不过,Eq比较特殊。在 GHCi 中(以及 GHCi 中的 only),Eqtype defaulting rules,因此,在没有任何附加信息的情况下,Eq a 将被假定为是 Eq ()(单位类型)。这仅适用于 GHCi。如果我们创建一个调用 quickCheckmain 函数,这是一个不明确的类型错误。但是,在 GHCi 中,它默认为 ().

现在,当然,() 只有一个实例,所以我们最终只用 () 的列表一遍又一遍地测试函数,因为你的两个函数总是会生成列表长度相同,测试通过。您可能想 运行 它在 Int 上。

*Main Test.QuickCheck> quickCheck (prop_sort6 :: [Int] -> Bool)
*** Failed! Falsified (after 5 tests and 1 shrink):
[0,1]

请注意,编译器标志 -Wtype-defaults(由 -Wall 启用)将警告您有关类型默认设置的信息,并让您知道出现问题。 -Wtype-defaults 活动:

*Main Test.QuickCheck> quickCheck prop_sort6

<interactive>:11:1: warning: [-Wtype-defaults]
    • Defaulting the following constraints to type ‘()’
        (Arbitrary a0)
          arising from a use of ‘quickCheck’ at <interactive>:11:1-21
        (Show a0)
          arising from a use of ‘quickCheck’ at <interactive>:11:1-21
        (Eq a0)
          arising from a use of ‘prop_sort6’ at <interactive>:11:12-21
    • In the first argument of ‘GHC.GHCi.ghciStepIO ::
                                  forall a. IO a -> IO a’, namely
        ‘(quickCheck prop_sort6)’
      In a stmt of an interactive GHCi command:
        it <- GHC.GHCi.ghciStepIO :: forall a. IO a -> IO a
              (quickCheck prop_sort6)
+++ OK, passed 100 tests.