Why not use smallcheck?
I guess I understand to little how depth works in smallcheck. In my tinycheck, you could simply define something like:
string :: TestCases String
string = arbitrary
<> (unwords <$> arbitrary)
<> (unlines . fmap unwords <$> arbitrary)
and you’d get strings, strings separated by spaces, strings separated by spaces and newlines both very early on. If it’s important to have spaces and newlines, then you can simply merge a generator that produces them into the default one.
Or if you need longer strings, you can do:
atLeast :: Int -> TestCases String
atLeast n = (<>) <$> replicateM n arbitrary <*> arbitrary
My point is, if you have requirements of some specific data showing up in the generator, then fix the generator. But I don’t see how there is a fundamental limitation to enumerating values.
Ok, here is maybe one thing: The concept of depth is maybe too restrictive. My approach here is to just enumerate in some order ad hoc, without keeping track precisely how deep we are going. If we need a few deeper values early on, just mix them into the generator. Keeping track of depth is not necessary, we can just restrict the number of test cases instead.
Discussion in the ATmosphere