QuickCheck: How to combine two generators?

I have two generators, gen_n and gen_arr :

 gen_n :: Gen Int gen_n = suchThat arbitrary (\i -> i >= 0 && i <= 10) gen_elem :: Gen Int gen_elem = suchThat arbitrary (\i -> i >= 0 && i <= 100) gen_arr :: Gen [Int] gen_arr = listOf gen_elem 

How can I combine these two in Gen (Int, [Int]) ?

 combine_two_gens :: Gen a -> Gen b -> Gen (a, b) 
+6
source share
1 answer

(i) You can use a normal functional / monadic composition to combine them:

 gen_comb :: Gen (Int, [Int]) gen_comb = (,) <$> gen_elem <*> gen_arr 

( Control.Applicative.liftA2 and Control.Monad.liftM2 are great too)

(ii) do not use suchThat to simply limit the range. This can be terribly inefficient as it simply generates random instances until the condition is met, discarding the rest. Instead, you can use elements :: [a] -> Gen a :

 gen_elem' :: Gen Int gen_elem' = elements [0..100] gen_arr' :: Gen [Int] gen_arr' = listOf gen_elem' gen_comb' :: Gen (Int, [Int]) gen_comb' = (,) <$> elements [0..100] <*> listOf (elements [0..100]) 

Update: As Zeta already mentioned, in this case we can do even better by using choose (0,100) ( choose :: Random a => (a, a) -> Gen a ) instead of elements [0..100] . See here or here for a complete list of generator combinators.


 *Main> sample gen_arr' [78] [2,27] [12,39] [92,22,40,6,18,19,25,13,95,99] ... *Main> sample gen_comb' (9,[23,3]) (11,[67,38,11,79]) (5,[96,69,68,81,75,14,59,68]) ... 

suchThat vs. elements :

 *Main> sample (suchThat arbitrary (\i -> i >= 10000 && i <= 10005)) ^CInterrupted. *Main> sample (elements [10000..10005]) 10003 10002 10000 10000 ... 

The generator suchThat did not suchThat anything.

+8
source

All Articles