-
-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC - Unify signatures accross arbitraries #992
Comments
Given the target described above, here is the list of arbitraries that would have to be updated. For each of them we give the current set of overloads and the strict destination: fc.integer (with constraints signature & codemod: ✔✔ since #1076)Existing signatures:
Target:
Signature
Signature
fc.nat (with constraints signature & codemod: ✔✔ since #1076)Existing signatures:
Target:
Signature
fc.float (with constraints signature & codemod: ✔✔ since #1068)Existing signatures:
Target:
Signatures:
fc.double (with constraints signature & codemod: ✔✔ since #1068)Existing signatures:
Target:
Signatures:
fc.bigInt (with constraints signature & codemod: ✔✔ since #1067)Existing signatures:
Target:
Signature
fc.bigUint (with constraints signature & codemod: ✔✔ since #1067)Existing signatures:
Target:
Signature
fc.*string (with constraints signature & codemod: ✔✔ since #1010)Existing signatures:
Target:
fc.stringOf (with constraints signature & codemod: ✔✔ since #1010)Existing signatures:
Target:
fc.json (with constraints signature & codemod: ✔✔ since #1023)Existing signatures:
Target:
Signature
fc.unicodeJson (with constraints signature & codemod: ✔✔ since #1023)Existing signatures:
Target:
Signature
fc.lorem (with constraints signature & codemod: ✔✔ since #1026)Existing signatures:
Target:
fc.option (with constraints signature: ✔ since #401, with codemod: ✔ since #1024)Existing signatures:
Target:
fc.array (with constraints signature & codemod: ✔✔ since #986)Existing signatures:
Target:
fc.set (with constraints signature & codemod: ✔✔ since #988)Existing signatures:
Target:
fc.subarray (with constraints signature & codemod: ✔✔ since #1011)Existing signatures:
Target:
fc.shuffledSubarray (with constraints signature & codemod: ✔✔ since #1011)Existing signatures:
Target:
fc.jsonObject (with constraints signature & codemod: ✔✔ since #1023)Existing signatures:
Target:
Signature
fc.unicodeJsonObject (with constraints signature & codemod: ✔✔ since #1023)Existing signatures:
Target:
Signature
fc.commands (with constraints signature: ✔ since #294, with codemod: ✔ since #1025)Existing signatures:
Target:
*with constraints signature: ✔ — an overload based on a constraints-object is already available on master branch |
In order to ease the migration of our users, fast-check will provide a simple to use codemod that should deal with most of the migration on its own: https://github.com/dubzzz/fast-check/tree/master/codemods/unify-signatures |
For Same, for |
💡 Idea
Aim
A single way to customize arbitaries of fast-check.
Contrary to existing:
fc.integer(1, 10)
(where 1 is for min and 10 for max) vsfc.object({maxKeys: 5})
(and notfc.object(5)
)Existing signatures
Today, the signatures of the built-in arbitraries coming with fast-check can be separated into two categories:
For the overloading ones you can think about
fc.array
. It accepts three main overloads:fc.array(arb)
,fc.array(maxLength)
andfc.array(minLength, maxLength)
.For the constraints-based ones, you can think about
fc.webUrl
. It only has two signatures:fc.webUrl()
andfc.webUrl(constraints)
.History
Most of the legacy arbitraries have been defined with an overloading strategy.
Over time this strategy proved weak: it did not scale to the increasing number of options required by new arbitraries, it appeared costly to develop (as overloading does not exist in JavaScript) and it was and is ambiguous for users.
Why costly to develop?
Let's suppose we want to develop something like
fc.array
with the overloads:fc.array(arb)
,fc.array(maxLength)
andfc.array(minLength, maxLength)
. Our code would be something like:This one is pretty simple as it only exposes two parameters but we already see how complex it can be to just know what would be the
minLength
ormaxLength
to apply.Why ambiguous?
The second main concern is ambiguity. For someone not knowing the API it is not very easy to know what means
2
in the following signaturesfc.array(fc.nat(), 2)
orfc.array(fc.nat(), 2, 2)
orfc.array(fc.nat(), 2, 3)
.As a consequence even in the tests of fast-check itself, we have been using
fc.array(arb, minLength, maxLength)
signature when just wanted to set themaxLength
(while we could have used a shorter version:fc.array(arb, maxLength)
).Signatures for
fc.set
might be even more ambiguous.Difficult to express everything
The main problem I faced with this API was: how do we let users specify only a
minLength
for arrays without specifying themaxLength
? While keeping the ability to set only themaxLength
without theminLength
.In the past, I thought about a placeholder-based system (see #89).
But actually even if this system seems elegant it does not remove the ambiguity of some signatures. Worst it cannot be really applied when the arbitrary comes with a high number of constraints (see
fc.object
orfc.webUrl
for instance).Target
As a conclusion, while the overloads way proved pretty useful and cool in the past, it seems that it will not be able to cover all the cases.
The aim of this RFC is to reach a single way to build our arbitaries in fast-check, so that whenever you discover a new arbitrary you already knows more or less how it should be called.
If we do a quick tour on existing ones, we can see that arbitraries have the following characteristics in terms of inputs:
fc.anything()
has zero compolsary options,fc.array(arb)
has one,fc.dictionary(keyArb, valueArb)
has two andfc.constantFrom(...args)
has manyThe proposal would be to adopt the following signatures everywhere:
arbitrary(...compulsaryOptions, constraints?)
(1) With maybe a special case (To Be Discussed) when we only have a single constraint, in which case we would have both
arbitrary(...compulsaryOptions, constraints?)
andarbitrary(...compulsaryOptions, min?)
(if our single constraint ismin
).(2) With maybe another special case (To Be Discussed): in the case we accept to have a signature containing all the optional parameters in row (if it makes sense) we could have something like:
arbitrary(...compulsaryOptions, ...optionalOptions)
(with none of them optional in this signature). The main question regarding this option is: does it make sense to specify all optional parameters together? For instance is it frequent in the case of an array to set both a min and a max?As a consequence, we would have the following signatures:
fc.array(arb, {minLength?, maxLength?} = {})
- TBD (2):fc.array(arb, minLength, maxLength)
fc.set(arb, {minLength?, maxLength?, compare?} = {})
- TBD (2):fc.array(arb, minLength, maxLength, compare)
fc.nat(arb, {max?} = {})
- TBD (1):fc.nat(arb, max)
And depreciate non-compliant ones while providing codemods to convert them easily. While neither (1) nor (2) has been totally accepted or rejected, the documentation of fast-check will show a non recommended - ambiguous message next to all the non compliant signatures (if the constraints-based version is available).
The text was updated successfully, but these errors were encountered: