11

I'm using QuickCheck 1 and I've got the following data types:

data A = ...
instance Arbitrary A where ...
data B = ...
instance Arbitrary B where ...
data C = C A B

Now I'd like to define an Arbitrary instance for C so that C values are generated using existing generators for A and B. I ended up doing this:

instance Arbitrary C where
  arbitrary = elements [(C a b) |
                        a <- generate 20 (System.Random.mkStdGen 0) arbitrary,
                        b <- generate 20 (System.Random.mkStdGen 0) arbitrary]

Is this explicit generation of a fixed number of values for A and B necessary, or is there a better way of combining existing Arbitraries into a new one?

Thomas M. DuBuisson
  • 64,245
  • 7
  • 109
  • 166
narthi
  • 2,188
  • 1
  • 16
  • 27

1 Answers1

20

I'd do it like this:

instance Arbitrary C
  where arbitrary = do a <- arbitrary
                       b <- arbitrary
                       return (C a b)

Although sclv's idea of using liftM2 from Control.Monad is probably better:

instance Arbitrary C
  where arbitrary = liftM2 C arbitrary arbitrary
dave4420
  • 46,404
  • 6
  • 118
  • 152
  • 18
    `arbitrary = C <$> arbitrary <*> arbitrary` -- applicative ftw! – sclv Feb 27 '11 at 18:22
  • 1
    Makes sense, thanks! I like the applicative solution even more but sadly it doesn't work in QuickCheck 1 since `Gen` was not an instance of `Applicative` back then. – narthi Feb 27 '11 at 21:19
  • sclv beat me to it. Applicatives can be very powerful and expressive. – Dan Burton Feb 27 '11 at 22:29