Not satisfied with my other answer, I have come up with an awesomer one.
-- arb.hs
import Test.QuickCheck
import Control.Monad (liftM)
data SimpleType = SimpleType Int Char Bool String deriving(Show, Eq)
uncurry4 f (a,b,c,d) = f a b c d
instance Arbitrary SimpleType where
arbitrary = uncurry4 SimpleType `liftM` arbitrary
-- ^ this line is teh pwnzors.
-- Note how easily it can be adapted to other "simple" data types
ghci> :l arb.hs
[1 of 1] Compiling Main ( arb.hs, interpreted )
Ok, modules loaded: Main.
ghci> sample (arbitrary :: Gen SimpleType)
>>>a bunch of "Loading package" statements<<<
SimpleType 1 'B' False ""
SimpleType 0 '\n' True ""
SimpleType 0 '\186' False "\208! \227"
...
Lengthy explanation of how I figured this out
So here's how I got it. I was wondering, "well how is there already an Arbitrary
instance for (Int, Int, Int, Int)
? I'm sure no one wrote it, so it must be derived somehow. Sure enough, I found the following in the docs for instances of Arbitrary:
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Well, if they already have that defined, then why not abuse it? Simple types that are merely composed of smaller Arbitrary data types are not much different than just a tuple.
So now I need to somehow transform the "arbitrary" method for the 4-tuple so that it works for my type. Uncurrying is probably involved.
Stop. Hoogle time!
(We can easily define our own uncurry4
, so assume we already have this to operate with.)
I have a generator, arbitrary :: Gen (q,r,s,t)
(where q,r,s,t are all instances of Arbitrary). But let's just say it's arbitrary :: Gen a
. In other words, a
represents (q,r,s,t)
. I have a function, uncurry4
, which has type (q -> r -> s -> t -> b) -> (q,r,s,t) -> b
. We are obviously going to apply uncurry4 to our SimpleType
constructor. So uncurry4 SimpleType
has type (q,r,s,t) -> SimpleType
. Let's keep the return value generic, though, because Hoogle doesn't know about our SimpleType. So remembering our definition of a
, we have essentially uncurry4 SimpleType :: a -> b
.
So I've got a Gen a
and a function a -> b
. And I want a Gen b
result. (Remember, for our situation, a
is (q,r,s,t)
and b
is SimpleType
). So I am looking for a function with this type signature: Gen a -> (a -> b) -> Gen b
. Hoogling that, and knowing that Gen
is an instance of Monad
, I immediately recognize liftM
as the monadical-magical solution to my problems.
Hoogle saves the day again. I knew there was probably some "lifting" combinator to get the desired result, but I honestly didn't think to use liftM (durrr!) until I hoogled the type signature.