Not satisfied with my other answer, I came up with an amazing option.
-- 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) = fabcd 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" ...
Long explanation of how I understood it
So, as I understand it. I was wondering: "How does an Arbitrary instance for (Int, Int, Int, Int) already exist? I'm sure no one wrote it, so I need to get it somehow. Of course, I found the following in docs for instances of an arbitrary file :
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Well, if they already have it, then why not abuse it? Simple types, which simply consist of smaller arbitrary data types, are not much different from a tuple.
So, now I need to somehow convert the “arbitrary” method to 4-tuples so that it works for my type. Most likely, launch is involved.
Stop. Hoogle time!
(We can easily define our own uncurry4 , so suppose we already have this to work 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 arbitrary :: Gen a . In other words, a represents (q,r,s,t) . I have a uncurry4 function that is of type (q -> r -> s -> t -> b) -> (q,r,s,t) -> b . Obviously, we will apply uncurry4 to our SimpleType constructor. So uncurry4 SimpleType has type (q,r,s,t) -> SimpleType . However, keep the return value because Hoogle does not know about our SimpleType. Therefore, remembering our definition of a , we have essentially uncurry4 SimpleType :: a -> b .
So, I have Gen a and the function a -> b . And I need a Gen b result. (Remember that for our situation, a is (q,r,s,t) , and b is SimpleType ). Therefore, I am looking for a function with such a signature of the type: Gen a -> (a -> b) -> Gen b . Hoogling, who , and knowing that Gen is an instance of Monad , I immediately recognize liftM as a monadic magic solution to my problem.
Hoogle is saving this day again. I knew that there was probably some kind of “lifting” combinator to get the desired result, but I honestly did not think to use liftM (durrr!) Until I pushed the type signature.