Using a custom generator versus an arbitrary instance in QuickCheck

Here is a simple function. It takes an input Int and returns a (possibly empty) list of pairs (Int, Int) , where the input Int is the sum of the cubic elements of any of the pairs.

 cubeDecomposition :: Int -> [(Int, Int)] cubeDecomposition n = [(x, y) | x <- [1..m], y <- [x..m], x^3 + y^3 == n] where m = truncate $ fromIntegral n ** (1/3) -- cubeDecomposition 1729 -- [(1,12),(9,10)] 

I want to check the property that the above is true; if I cube each element and summarize any tuple returned, then I return my input:

 import Control.Arrow cubedElementsSumToN :: Int -> Bool cubedElementsSumToN n = all (== n) d where d = map (uncurry (+) . ((^3) *** (^3))) (cubeDecomposition n) 

For runtime reasons, I would like to limit the input of Int to a certain size when checking this with QuickCheck. I can determine the appropriate type and instance of Arbitrary :

 {-# LANGUAGE GeneralizedNewtypeDeriving #-} import Test.QuickCheck newtype SmallInt = SmallInt Int deriving (Show, Eq, Enum, Ord, Num, Real, Integral) instance Arbitrary SmallInt where arbitrary = fmap SmallInt (choose (-10000000, 10000000)) 

And then, I think, I need to determine the versions of the function and properties that use SmallInt , not Int :

 cubeDecompositionQC :: SmallInt -> [(SmallInt, SmallInt)] cubeDecompositionQC n = [(x, y) | x <- [1..m], y <- [x..m], x^3 + y^3 == n] where m = truncate $ fromIntegral n ** (1/3) cubedElementsSumToN' :: SmallInt -> Bool cubedElementsSumToN' n = all (== n) d where d = map (uncurry (+) . ((^3) *** (^3))) (cubeDecompositionQC n) -- cubeDecompositionQC 1729 -- [(SmallInt 1,SmallInt 12),(SmallInt 9,SmallInt 10)] 

This works great, and the standard 100 tests pass as expected. But it seems there is no need to define a new type, instance, and function when I really need a special generator. So I tried this:

 smallInts :: Gen Int smallInts = choose (-10000000, 10000000) cubedElementsSumToN'' :: Int -> Property cubedElementsSumToN'' n = forAll smallInts $ \m -> all (== n) (dm) where d = map (uncurry (+) . ((^3) *** (^3))) . cubeDecomposition 

Now, the first few times that I ran this, everything worked fine, and all the tests pass. But in subsequent episodes, I observed setbacks. A reliable test size reliably finds one:

 *** Failed! Falsifiable (after 674 tests and 1 shrink): 0 8205379 

I got a little confused here due to having two abbreviated inputs - 0 and 8205379 - returned from QuickCheck, where I would intuitively expect it. In addition, these inputs work as predicted (in my indicative quality, at least):

 *Main> cubedElementsSumToN 0 True *Main> cubedElementsSumToN 8205379 True 

So it seems that there is a problem in the property that uses the custom Gen that I defined.

What I did wrong?

+4
source share
1 answer

I quickly realized that the property, as I wrote it, was clearly wrong. Here's the right way to do this using the original cubedElementsSumToN property:

 quickCheck (forAll smallInts cubedElementsSumToN) 

which reads quite naturally.

+2
source

Source: https://habr.com/ru/post/1445669/


All Articles