I would like to combine two custom generators of different data type, but which are grouped together in another datatype.
In the following example, I would like to use the generator for Legumes
and AnimalProteins
to create another one for Proteins. choose
and elements
does not work as the type are different. Casting the generators as gen Proteins
does not work either.
data AnimalProteins = Beef | Chicken | Fish
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas
data Proteins = AnimalProteins | Legumes deriving (Show)
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef , Chicken , Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans , RedBeans , Lentils , Chickpeas]
-- This does not work !
rProteins :: Gen Proteins
rProteins = choose (rLegumes, rAnimalProteins)
The solution may be simple but I'm quite stuck here as a beginner. Thanks !
Assuming that you intend to model Proteins
as a choice between AnimalProteins
and Legumes
, you probable need something like this instead:
data AnimalProteins = Beef | Chicken | Fish deriving (Show, Eq)
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas deriving (Show, Eq)
data Proteins = AP AnimalProteins | L Legumes deriving (Show, Eq)
You can combine generators like this:
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef, Chicken, Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans, RedBeans, Lentils, Chickpeas]
rProteins :: Gen Proteins
rProteins = oneof [fmap AP rAnimalProteins, fmap L rLegumes]
The rAnimalProteins
and rLegumes
are as you've already defined them. You can 'lift' them into two separate Gen Proteins
values with fmap
, since Gen
is a Functor
.
For example, fmap AP rAnimalProteins
maps the Gen Legumes
value to a Gen Proteins
value by mapping every generated Legumes
value by wrapping it with the AP
data constructor. While it's a Gen Proteins
value, it'll always create AP
values, that is, either AP Beef
, AP Chicken
, or AP Fish
.
Likewise, fmap L rLegumes
lifts rLegumes
from Gen Legumes
to Gen Proteins
. It'll always generate one of the values L WhiteBeans
, L RedBeans
, L Lentils
, or L Chickpeas
.
By using oneof
, you compose the two specialised generators to a more complex one that randomly selects one of those generators to generate a value.