Skip to content
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

API Suggestion: Creating Arbitraries of Fundamental Types #2

Open
jlink opened this issue Aug 12, 2024 · 2 comments
Open

API Suggestion: Creating Arbitraries of Fundamental Types #2

jlink opened this issue Aug 12, 2024 · 2 comments

Comments

@jlink
Copy link
Contributor

jlink commented Aug 12, 2024

In Jqwik 1 all fundamental type arbitraries were rooted in class Arbitraries.
Therefore, you create an arbitrary for Strings with Arbitraries.strings() and integers with Arbitraries.integers() and so on.
Also, more fundamental arbitrary types, like simply choosing one of several values or selecting one arbitraries from many,
started there.
This lead to many (43) static public methods in Arbitraries; too many to navigate easily.

In order to make navigation easier, I suggest to put those fundamental creation methods on several base classes:

  • Class Numbers will contain creation methods for all Java number types, e.g.

    • Numbers.integers()
    • Numbers.bigDecimals()
    • etc.
  • Class Strings, with

    • Strings.strings()
    • Strings.unicodCodepoints()
    • Strings.chars()
  • Class Values, with

    • Values.just(..)
    • Values.of(..)
    • Values.frequency(..)
    • Values.oneOf(..) for choosing among arbitraries
    • Values.lazy(..)
    • Values.defaultFor(..)
    • etc.

Other base classes - potentially located in modules outside the core - could be:

  • Maps for creating a map from one type to another
  • Dates, Times, ....

Design Questions

  • Does distributing static creation methods over several class help discoverability or harm it?
  • Are there other approaches with good discoverability, e.g. starting from one main arbitrary builder function and specializing from there. E.g.: Arbitraries.numbers().bigIntegers() ?
@SimY4
Copy link

SimY4 commented Aug 12, 2024

IMO it's a loss of discoverability. There's not better way to put it other than in a class as static functions.

One thing I've experimented with in the past is having static nested classes instead of static methods.

interfaces Arbitrary<A> {
  ...

  final class Numbers {
    ...
  }
}


var arb = Arbitrary.Numbers.integral()

It saves you from using braces in between "words" making it look slightly closer to normal text. But discovery is still worse than just a kitchen sink with static methods.

To add to that of I may, I'd suggest to unfinalize the Arbitraries class and make its constructor accessible so that users could extend it. Similar thing is followed by all of the assertions libraries. E.g. so this become possible:

class MyArbitraries extends Arbitraries {
  ...
}

The latter can be done for jqwik v1. 😉

@jlink
Copy link
Contributor Author

jlink commented Aug 13, 2024

One thing I've experimented with in the past is having static nested classes instead of static methods.

I like this idea. The drawback I see is that only the core module would be able to use that approach. Additional modules would have to come with their own base class(es).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants