[Haskell-cafe] SmallCheck Depth

Roman Cheplyaka roma at ro-che.info
Thu Feb 5 18:29:38 UTC 2015

On 05/02/15 16:29, Omari Norman wrote:
> On Thu, Feb 5, 2015 at 6:06 AM, Roman Cheplyaka <roma at ro-che.info> wrote:
>> The choice of what belongs to what depth is rather arbitrary.
>> You say you'd expect all three constructors to be of depth 0. What about
>>   data A = A1 .. A50
>> ?
>> What about Char or Int?
>> See the problem?
> I see the issue, but I think the current SmallCheck documentation does
> not acknowledge this issue and the documentation in fact obstructs
> understanding of the way the library currently works.

I simply didn't think it was important for the end users.

Granted, having a nice, principled answer to the question "what is
depth" would be nice. I spent a fair bit of time trying to figure it
out, but hadn't found it.

But practically speaking, you can just look at the generated values
(using 'list') and see which depth you need (or whether you need to
redefine the instance, as I often do for Char).

> First, Runciman 2008 does not say that the choice of what belongs to
> what depth is arbitrary.  Rather, it states that "the depth of a
> zero-arity construction is zero, and the depth of a positive-arity
> construction is one greater than the maximum depth of a component
> argument."  Thus the depth of True would be zero.  In
> data A = A1 .. A50
> the depth of each value would be zero.
> The Runciman 2008 theory holds perfectly for Int if one conceptualizes
> Int to be built as follows:
> data Sign = Pos | Neg
> data Nat = One | Succ Nat
> data Int = Zero | NonZero Sign Nat

Except that's not what Int is. I'm sure one can find a mapping that
justifies almost any instance.

> Upon further examination of the current SmallCheck source, I see that
> 'cons0' decreases the depth, while its counterpart in the original
> SmallCheck did not.  What I was wondering is whether this change was
> made for any particular reason.

IIRC, it was mostly for consistency. cons1, cons2 etc. all decrement the
depth; why shouldn't cons0?

If depth(Just x) = 1 + depth x, it sounds fair that
   depth(Nothing) = 1 + depth(<really nothing>) = 1

> "Test.SmallCheck.Series" still states
> that "For data values, [depth] is the depth of nested constructor
> applications."

True is a single constructor, so its depth is 1.
Just True is a constructor application of depth 2.
And so on.
So this still mostly holds (ignoring cases like Int).

That said, the docs could definitely be improved; patches are welcome.

> Perhaps, then, if the current theory underlying SmallCheck is that
> depth is just some size-limiting thing, the documentation should say
> something like "Depth is a non-negative integer.  You use depth to
> make progressively 'larger' values.  What 'larger' means is up to you.
> Runciman 2008 said it means the depth of nested constructor
> applications, but that is not always practically sensible.  You
> should, however, ensure that all the values included at @depth n@ are
> also included in @depth n + 1 at . "

Sounds sensible.

> Even with an explanation like that,
> though, I don't see why @list 0@ should sometimes return values (as it
> does for () and Int) and sometimes not (as it does with Bool).  That's
> a puzzling behavior that did not exist in the original SmallCheck.

*Test.SmallCheck> series 0 :: [Bool]
*Test.SmallCheck> series 0 :: [Either Bool Bool]

- Roman

More information about the Haskell-Cafe mailing list