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

Ability to add generators for generic types #77

Open
AlexeyRaga opened this issue Nov 30, 2024 · 5 comments
Open

Ability to add generators for generic types #77

AlexeyRaga opened this issue Nov 30, 2024 · 5 comments

Comments

@AlexeyRaga
Copy link
Contributor

It'd be nice if it was possible to register generators for generic types to AutoGenConfig.

For example, if I have my own generic type, like Id<T> then I can create Gen<Id<T>> for it, but there is currently no way to add it to AutoGenConfig.

Do you think something like that can be implemented?

@TysonMN
Copy link
Member

TysonMN commented Nov 30, 2024

Not sure.

For how many values of T are you currently expressing that mapping efficiently?

@AlexeyRaga
Copy link
Contributor Author

"how many" - does it really matter?

There are many contexts, with multiple possible values...
Id<T>, ApiResponse<T>, Tree<T>, ParserResult<T>...

These can be types of properties in other data types:

type Payment = {
    id : Id<Payment>
    orderId : Id<Order>
    ....
}

Writing specific generators every time is not very convenient.

And it is even less convenient when we put C# into the picture, because C# doesn't have built-in (with special support by HH) types for Option or Result and people use library types (not specially supported by HH).

Being able to register generators for generic types would trivialise and solve this problem: once defined generator would "just work" everywhere.

FSCheck also has this ability: they reflect generators from static properties of a class, and those could be of generic types, something like

type MyGenerators =
  static member IdGen<T>() : Arbitrary<Id<T>> = ...
  static member EdgeGen<TNode, TWeight>() : Arbitrary<Edge<TNode, TWeight>> = ...

@AlexeyRaga
Copy link
Contributor Author

I dug up an example of how it is done with FSCheck in C#, to illustrate the value from the real life code:

public static class PreludeArb
{
    public static Arbitrary<Either<TLeft, TRight>> EitherArb<TLeft, TRight>() =>
        Gen.OneOf(
            ArbMap.Default.GeneratorFor<TLeft>().Select(Either.Left<TLeft, TRight>),
            ArbMap.Default.GeneratorFor<TRight>().Select(Either.Right<TLeft, TRight>)).ToArbitrary();

    public static Arbitrary<Option<T>> OptionArb<T>() =>
        Gen.OneOf(
            ArbMap.Default.GeneratorFor<T>().Select(Option.Some), 
            Gen.Constant(Option.None<T>())).ToArbitrary();

    public static Arbitrary<NonEmptyList<T>> NonEmptyListArb<T>() =>
        ArbMap.Default
            .GeneratorFor<T>()
            .NonEmptyListOf()
            .Select(xs => new NonEmptyList<T>(xs[0], xs.Skip(1)))
            .ToArbitrary();
}

Unfortunately, doing it with Hedgehog is not really possible at the moment...

@TysonMN
Copy link
Member

TysonMN commented Nov 30, 2024

"how many" - does it really matter?

Yes. If you want this feature so you can write one line of code instead of two, then your use case is not sufficient justification for this feature. If you would avoid writing 100 lines of code, then that is sufficient justification.


Want to submit PR with this feature?

@AlexeyRaga
Copy link
Contributor Author

If you want this feature so you can write one line of code instead of two, then your use case is not sufficient justification for this feature. If you would avoid writing 100 lines of code, then that is sufficient justification.

It is more on a conceptual level here, as you probably see now. Can be 1 line, can be hundreds, depends on the case.
In my specific use cases it'd be a lot of repetitive lines across multiple repositories.
But, really, why List<T> is OK and Id<T> or NonEmptyList<T> or Tree<T> is suddenly not...

Want to submit PR with this feature?

I could try, if there is a guidance on how it can/should be done in this library.

I am a bit of afraid of just starting to work on it, based on my previous experiences, where I was discouraged with some bureaucracy like splitting/joining commits, nuances about how things should/should not be written, etc.

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

No branches or pull requests

2 participants