I have two generators, gen_n
& 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 into a Gen (Int, [Int])
?
combine_two_gens :: Gen a -> Gen b -> Gen (a, b)
(i) You can use normal functorial/monadic composition to combine them:
gen_comb :: Gen (Int, [Int])
gen_comb = (,) <$> gen_elem <*> gen_arr
(Control.Applicative.liftA2
and Control.Monad.liftM2
are also fine, of course)
(ii) don't use suchThat
to merely constrain a range. It can be terribly inefficient, as it just generates random instances until the condition is met, discarding the rest. Instead, you could 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 remarked below, we can do even better in this case, by using choose (0,100)
(choose :: Random a => (a, a) -> Gen a
) instead of elements [0..100]
. See here, or here for the full 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 suchThat
generator didn't output anything.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With