Only generate positive integers with QuickCheck
Asked Answered
Z

1

12

We have two functions that compare two different power functions, and return true if they return the same value (on the same input).

Then we have two other functions that test these functions against two lists to see if there is any value that doesn't return true.

But instead of using lists that use a range [1..100], we would like to use QuickCheck.

Is it possible to make QuickCheck only return positive integers?

Code:

comparePower1 :: Integer -> Integer -> Bool
comparePower1 n k = power n k == power1 n k

comparePower2 :: Integer -> Integer -> Bool
comparePower2 n k = power n k == power2 n k

testing1 = and [comparePower1 n k | n <- [0..100], k <- [0..100]]
testing2 = and [comparePower2 n k | n <- [0..100], k <- [0..100]]
Zamboanga answered 2/9, 2016 at 11:39 Comment(3)
maybe #12467080 will help ?Lafrance
@TomaszLewowski Not really. I did not understand this type class/definition (Integral a, Show a, Read a) => ...Zamboanga
how about using suchThat modifier for generator?Lafrance
V
16

QuickCheck has support for Positive numbers, but for the sake of this tutorial I will show you how to create your own Generator. One of the main features of QuickCheck is that you can design your own generator to output just what you need. For instance

genPos :: Gen Int
genPos = abs `fmap` (arbitrary :: Gen Int) `suchThat` (> 0)

Then you can create your own list genereator

genListOfPos :: Gen [Int]
genListOfPos = listOf genPos

Finally you can use forAll, pass the generator and profit.

main :: IO ()
main = do
  quickCheck $ forAll genPos $ \x -> x > 0
  quickCheck $ forAll genListOfPos $ all (> 0)
Viccora answered 2/9, 2016 at 12:21 Comment(6)
Thx, it worked! A question: Why dont I need to apply the new list to the testing function, or atleast delete the existing list there ([1..100])? Or maybe my question should be, what exactly is that function with that list doing now?Zamboanga
all (> 0) expands to \xs -> all (\x -> x > 0) xs which means apply \x -> x > 0 to each element of xs and then run all to check if all elements are TrueViccora
For the sake of efficiency, it might be better to generate a Word, add 1, cast to Int, take the absolute value, and then toss zero. The reason this might be better is that I believe QuickCheck normally tries to choose mostly "small" values, so you could have a lot of zeros to throw away. You'd have to check some details before taking this advice, however--I could be wrong.Pollinize
It would be nice to try if this is true. Another solution is to just add 1 to the generated number (except if it's maxBound :: int) instead of filter > 0.Viccora
So how exactly do you invoke this on GHCi? quickCheck (forAll genListOfPos (all (> 0)) comparePower2?Smoko
Define genPos and genListOfPos with let (e.g. let genPos = ...) and then you can just runquickCheck $ forAll genListOfPos $ all (> 0).Viccora

© 2022 - 2024 — McMap. All rights reserved.