Here's one possibility. We'll define a new class for types that can build a size-dependent random value. Then you can make a type-level list or tree or whatever and declare one Arbitrary
instance for these once and for all.
import Control.Monad
import Test.QuickCheck
class SizedArbitrary a where
sizedArbitrary :: Int -> Gen a
instance Arbitrary a => SizedArbitrary [a] where
sizedArbitrary n = replicateM n arbitrary
data Branch a b = a :+ b deriving (Eq, Ord, Show, Read)
instance (SizedArbitrary a, SizedArbitrary b) => SizedArbitrary (Branch a b) where
sizedArbitrary n = liftM2 (:+) (sizedArbitrary n) (sizedArbitrary n)
instance (SizedArbitrary a, SizedArbitrary b) => Arbitrary (Branch a b) where
arbitrary = arbitrarySizedIntegral >>= sizedArbitrary . abs
Then we can load it up in ghci and check out that it works:
*Main> let allSameLength (xs:xss) = all (==length xs) (map length xss)
*Main> quickCheck (\(xs :+ ys) -> allSameLength [xs, ys])
+++ OK, passed 100 tests.
*Main> quickCheck (\(ws :+ xs :+ ys :+ zs) -> allSameLength [ws, xs, ys, zs])
+++ OK, passed 100 tests.