In Haskell, Is there a "standard" library/package for generating Random
/Arbitrary
enums?
I wrote the following code, but I can't believe I'm the first person to have this need or solve it (and I'm not certain my solution is totally correct). Also, I hope that an existing solution has other nice functions alongside it.
Here's a pair of functions to choose a random value from an Enum type:
enumRandomR :: (RandomGen g, Enum e) => (e, e) -> g -> (e, g)
enumRandomR (lo,hi) gen =
let (int, gen') = randomR (fromEnum lo, fromEnum hi) gen in (toEnum int, gen')
enumRandom :: (RandomGen g, Enum e) => g -> (e, g)
enumRandom gen =
let (int, gen') = random gen in (toEnum int, gen')
and here are instances for System.Random.Random
and Test.QuickCheck.Arbitrary
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}
instance (Enum a, Bounded a) => Random a where
random = enumRandom
randomR = enumRandomR
instance (Enum a, Bounded a) => Arbitrary a where
arbitrary = choose (minBound, maxBound)
Here is an example Bounded
, Enum
type
data Dir = N | E | S | W
deriving (Show, Enum, Bounded)
and here is a test of Random/Arbitrary methods
> import Test.QuickCheck
> sample (arbitrary:: Gen Dir)
N
E
N
S
N
E
W
N
N
W
W
I'm not delighted that my solution relies on these extensions:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}"
because:
- Constraint is no smaller than the instance head
in the constraint: Enum a
(Use -XUndecidableInstances to permit this)
,
- Overlapping instances for Random Int
arising from a use of `randomR'
Matching instances:
instance Random Int -- Defined in System.Random
instance (Enum a, Bounded a) => Random a
, and
- Illegal instance declaration for `Random a'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use -XFlexibleInstances if you want to disable this.)
Is there a better way? Does my solution fail for some (more "exotic") Bounded Enum types than my simple example?
The standard workaround in situations like this is to create a newtype
wrapper and provide instances for that instead.
{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- avoid some boilerplate
newtype Enum' a = Enum' a
deriving (Bounded, Enum, Show)
instance (Enum a, Bounded a) => Random (Enum' a) where
random = enumRandom
randomR = enumRandomR
instance (Enum a, Bounded a) => Arbitrary (Enum' a) where
arbitrary = choose (minBound, maxBound)
Of course, that approach requires some extra wrapping and unwrapping when using the new type, but for use with QuickCheck, that shouldn't be too bad, as you typically only need to pattern match once per property:
prop_foo (Enum' x) = ... -- use x as before here
QuickCheck exports a function
arbitraryBoundedEnum :: (Bounded a, Enum a) => Gen a
This function may reasonably be considered "standard".
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