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]]
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
mrfr
  • 1,724
  • 2
  • 23
  • 44

1 Answers1

15

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)
mariop
  • 3,195
  • 1
  • 19
  • 29
  • 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? – mrfr Sep 02 '16 at 12:53
  • `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 `True` – mariop Sep 02 '16 at 13:10
  • 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. – dfeuer Sep 02 '16 at 19:29
  • 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`. – mariop Sep 03 '16 at 00:19
  • So how exactly do you invoke this on GHCi? quickCheck (forAll genListOfPos (all (> 0)) comparePower2? – OthmanEmpire Oct 06 '20 at 15:36
  • Define `genPos` and `genListOfPos` with `let` (e.g. `let genPos = ...`) and then you can just run`quickCheck $ forAll genListOfPos $ all (> 0)`. – mariop Oct 07 '20 at 18:35