Почему QuickCheck сдаётся?

Я использую QuickCheck для проверки следующей программы:

{-# LANGUAGE TemplateHaskell #-}

import Test.QuickCheck
import Test.QuickCheck.All

elementAt :: (Integral b) => [a] -> b -> a
elementAt [x] _ = x
elementAt (x:xs) 1 = x
elementAt (x:xs) b = elementAt xs (b - 1)

prop_elementAt xs b = length xs > 0 && b >= 0 && b < length xs ==> elementAt xs (b + 1) == xs !! b

main = $(quickCheckAll)

Хотя ответ меняется, я постоянно получаю сообщение

*** Gave up! Passed only x tests.

Это что-то, о чем я должен беспокоиться? Или характер ввода теста диктует, как долго QuickCheck будет работать?

Ответ 1

Как работает ==>, первый quickcheck генерирует случайные значения для xs и b, а затем проверяет, выполняется ли только предикат length xs > 0 && b >= 0 && b < length xs, тогда он проверяет выполнимость свойства.

Поскольку существует предел для того, сколько тестовых случаев он будет генерировать, может случиться так, что много раз этот предикат не выполняется. Таким образом, quickcheck сбрасывается, прежде чем генерировать достаточно действительные тестовые файлы (удовлетворяющие предикату).

Вместо этого вы должны объявить экземпляр Arbitrary для newtype для генерации только тестовых полей, удовлетворяющих этим предикатам.

{-# LANGUAGE TemplateHaskell #-}

import Test.QuickCheck
import Test.QuickCheck.All

elementAt :: (Integral b) => [a] -> b -> a
elementAt [x] _ = x
elementAt (x:xs) 1 = x
elementAt (x:xs) b = elementAt xs (b - 1)

prop_elementAt (Foo xs b) = elementAt xs (b + 1) == xs !! b

data Foo a b = Foo [a] b deriving (Show)

instance (Integral b, Arbitrary a, Arbitrary b) => Arbitrary (Foo a b) where
  arbitrary = do
    as <- listOf1 arbitrary           -- length xs > 0
    b <- choose (0,length as - 1)     -- b >= 0 and b < length xs
    return (Foo as $ fromIntegral b)

main = $(quickCheckAll)