qcheck icon indicating copy to clipboard operation
qcheck copied to clipboard

Combinator naming principles

Open jmid opened this issue 4 months ago • 17 comments

Before setting out on "The Great Combinator Renaming" :tm: we should document the naming principles.

Here's a cleaned up version distilled from past lessons in #223 and #243:

  • Generator names should align with type names (bool, char, ... list, option) - to be as predictable as possible
  • We should have short, unparameterized generators (int, string, ...) to lower the barrier to entry
  • Specialized generators also start with the type name, but use a consistent suffix (_pos, _neg, _size, _of, ...) as this will help find them, also when using tab-completion
  • We may include a few shorthand names for convenience (int_pos->nat, option->opt, ...)
  • Overall we aim to be as consistent as possible, e.g., offering similar signatures across generator interfaces (QCheck.Gen, QCheck.arbitrary, QCheck2.Gen)

We've already used these principles, e.g., in naming the bytes and string combinators in #245 as well as in #307 and #308, so they should not come as a surprise.

Finally: I plan to add these naming principles to the documentation.

jmid avatar Nov 04 '25 13:11 jmid

I've just opened #367 to address float names.

jmid avatar Nov 04 '25 15:11 jmid

FTR, the unit and bool situation is consistent (it is admittedly also rather simple):

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
unit unit t unit arbitrary unit t
bool bool t bool arbitrary bool t

jmid avatar Nov 04 '25 15:11 jmid

I've now opened #369 to address char names.

jmid avatar Nov 05 '25 23:11 jmid

I've opened #370 to address option consistency.

For the result type the situation is nice, simple, and already consistent: 🥳

Combinator name Type signiture
QCheck.Gen.result ?ratio:float -> 'a t -> 'e t -> ('a, 'e) result t
QCheck.result ?ratio:float -> 'a arbitrary -> 'e arbitrary -> ('a, 'e) result arbitrary
QCheck2.Gen.result ?ratio:float -> 'a t -> 'e t -> ('a, 'e) result t

jmid avatar Nov 06 '25 11:11 jmid

#371 took care of bytes renaming

jmid avatar Nov 07 '25 18:11 jmid

I've just opened #372 to address string renaming

jmid avatar Nov 07 '25 18:11 jmid

For int32 and int64 the situation is as follows:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
int32 int32 t int32 arbitrary int32 t
ui32 int32 t - int32 t
int64 int64 t int64 arbitrary int64 t
ui64 int64 t - int64 t

As such their situation is reasonably consistent.

Furthermore

  • QCheck.Gen.ui32
  • QCheck2.Gen.ui32
  • QCheck.Gen.ui64 and
  • QCheck2.Gen.ui64

are already deprecated since 0.24 - and should be removed in our 1.0 release.

jmid avatar Nov 07 '25 18:11 jmid

I've opened #373 with array renaming

jmid avatar Nov 18 '25 14:11 jmid

and now #374 with list renaming

jmid avatar Nov 18 '25 16:11 jmid

Good news! The tuple generators are consistent:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
pair yes yes yes
triple yes yes yes
quad yes yes yes
tup2 yes yes yes
tup3 yes yes yes
tup4 yes yes yes
tup5 yes yes yes
tup6 yes yes yes
tup7 yes yes yes
tup8 yes yes yes
tup9 yes yes yes

(I've left out the long type signatures though)

jmid avatar Nov 18 '25 16:11 jmid

I've just opened a PR for int renaming: https://github.com/c-cube/qcheck/pull/376

jmid avatar Nov 27 '25 20:11 jmid

Going over some of the remaining generator names, the situation for sized, fix, and delay generators is as follows:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
sized 'a sized -> 'a t - 'a sized -> 'a t
sized_size int t -> 'a sized -> 'a t - int t -> 'a sized -> 'a t
fix (('a -> 'b t) -> ('a -> 'b t)) -> 'a -> 'b t - (('a -> 'b t) -> 'a -> 'b t) -> 'a -> 'b t
delay (unit -> 'a t) -> 'a t - (unit -> 'a t) -> 'a t

Generally, these are for more low-level generator manipulation, e.g., sized does not exist for QCheck.arbitrary. As such it seems reasonable to leave them out of QCheck.arbitrary.

jmid avatar Dec 02 '25 15:12 jmid

The situation is similar for the let operators:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
( let+ ) 'a t -> ('a -> 'b) -> 'b t - 'a t -> ('a -> 'b) -> 'b t
( and+ ) 'a t -> 'b t -> ('a * 'b) t - 'a t -> 'b t -> ('a * 'b) t
( let* ) 'a t -> ('a -> 'b t) -> 'b t - 'a t -> ('a -> 'b t) -> 'b t
( and* ) 'a t -> 'b t -> ('a * 'b) t - 'a t -> 'b t -> ('a * 'b) t

It seems reasonable to keep it like this.

jmid avatar Dec 02 '25 15:12 jmid

The subset and split are only present in QCheck.Gen:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
range_subset size:int -> int -> int -> int array t - -
array_subset int -> 'a array -> 'a array t - -
nat_split2 int -> (int * int) t - -
pos_split2 int -> (int * int) t - -
nat_split size:int -> int -> int array t - -
pos_split size:int -> int -> int array t - -

Issue https://github.com/c-cube/qcheck/issues/238 tracks their addition to QCheck2.Gen and I'll leave it at that for now.

jmid avatar Dec 02 '25 15:12 jmid

The situation for the generate* debug combinators is as follows:

Combinator name QCheck.Gen QCheck.arbitrary QCheck2.Gen
generate ?rand:Random.State.t -> n:int -> 'a t -> 'a list - ?rand:Random.State.t -> n:int -> 'a t -> 'a list
generate1 ?rand:Random.State.t -> 'a t -> 'a - ?rand:Random.State.t -> 'a t -> 'a
generate_tree - - ?rand:Random.State.t -> 'a t -> 'a Tree.t

Since there is no Tree.t type in QCheck this is reasonably consistent.

jmid avatar Dec 02 '25 15:12 jmid

https://github.com/c-cube/qcheck/pull/380 addressed renaming of map combinators

jmid avatar Dec 10 '25 20:12 jmid

I've just opened #381 for renaming of generator-returning shrink combinators

jmid avatar Dec 10 '25 20:12 jmid