-
-
Notifications
You must be signed in to change notification settings - Fork 64
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
Add fluent combinator API #46
Comments
I like the idea. Just playing with potential APIs. Consider those domain classes
The test could look like this:
What do you think? There's a potential problem with the builder instance being reused but one can solve that with lazy generation. |
That would work well for my use case. I'm slightly turned off by the separated I was feeling pretty proud of myself with how general my original proposal was. Hypothetically, it could be useful for things other than working with builders. But, it's hard to synthesize another good use case for chaining combinators together. |
@jdmarble Could you elaborate the negative implications of use/in? I introduced use/in because it reads a bit more fluent (for my taste) and it's a bit closer to 'Combinators.combine(...).as(...)'. And the generality that was in your approach is still preserved. |
Yes, I can elaborate. Consider the return type of
Now I see that it is nearly as general as my approach. One thing that tripped me up was that the name ( What if we want to combine more than two arbitraries? It would be difficult to do this in a type safe way without having public <U, A1> Arbitrary<U> map(Combinators.F2<T, A1, U> mapper, Arbitrary<A1> a1) {
return Combinators.combine(this, a1).as(mapper);
}
public <U, A1, A2> Arbitrary<U> map(Combinators.F3<T, A1, A2, U> mapper, Arbitrary<A1> a1, Arbitrary<A2> a2) {
return Combinators.combine(this, a1, a2).as(mapper);
}
// ... and so on ... Here's another (contrived) use case for this idea that isn't exactly a builder: return Combinators.withBuilder(Calendar.getInstance())
.use(Arbitraries.integers().between(1990, 2000))
.in((calendar, year) -> calendar.set(Calendar.YEAR, year))
.use(Arbitraries.integers().between(5, 15))
.in((calendar, weekOfYear) -> calendar.set(Calendar.WEEK_OF_YEAR, weekOfYear))
.use(Arbitraries.integers().between(2, 5))
.in((calendar, dayOfWeek) -> calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek))
.build(calendar -> calendar); |
@jdmarble Thanks for the valuable feedback. A statically typed split between use and in requires an additional class indeed. This is a bit more complex to implement. The reason I prefer this API is the symmetry to With a single parameter the implementation cost is not very high either. Here's my prototypical implementation (I chose a new entrance class
Having a As for naming, I'm a bit torn between BuilderCombinator, ArbitraryBuilder and something with "Fluent" in it. My suggestion: I include something along the lines sketched above as experimental feature, and you - and others - can give me feedback about how it feels in real usage. |
1 similar comment
@jdmarble Thanks for the valuable feedback. A statically typed split between use and in requires an additional class indeed. This is a bit more complex to implement. The reason I prefer this API is the symmetry to With a single parameter the implementation cost is not very high either. Here's my prototypical implementation (I chose a new entrance class
Having a As for naming, I'm a bit torn between BuilderCombinator, ArbitraryBuilder and something with "Fluent" in it. My suggestion: I include something along the lines sketched above as experimental feature, and you - and others - can give me feedback about how it feels in real usage. |
For the moment, I'll go with Implemented in a5348f2 Available in latest 1.1.1-SNAPSHOT |
Will be available in 1.1.1 (deployment ongoing). Feel free to reopen if something should be improved. |
Testing Problem
I have some classes with many fields that are created using the builder pattern. When the number of fields is less than 9, I can create arbitraries easily enough:
There's some extra boilerplate, but it's not a big problem.
When there are many fields, I've been using this pattern:
I've further reduced boilerplate with this wrapper class:
This allows for some nicer code:
Suggested Solution
I'd rather this be integrated into
Arbitrary
itself. Then I could just do:Discussion
The current boilerplate per setter isn't that painful until you have many dozens of fields.
The text was updated successfully, but these errors were encountered: