-
Notifications
You must be signed in to change notification settings - Fork 37
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
Introduce {Mono,Flux}Map{,NotNull}
Refaster rules
#142
Conversation
Thanks for opening a PR @cernat-catalin. Just to let you know we saw it; next week I'll take a look at this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tnx for suggesting this! Added a commit.
...r-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/ReactorTemplates.java
Outdated
Show resolved
Hide resolved
...r-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/ReactorTemplates.java
Outdated
Show resolved
Hide resolved
...b/src/test/resources/tech/picnic/errorprone/refastertemplates/ReactorTemplatesTestInput.java
Outdated
Show resolved
Hide resolved
...r-prone-contrib/src/main/java/tech/picnic/errorprone/refastertemplates/ReactorTemplates.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some open questions/thoughts; need to leave the metro now :)
|
||
@BeforeTemplate | ||
Mono<S> before(Mono<T> mono) { | ||
return mono.flatMap(x -> Mono.just(transformation(x))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same applies to Flux.just
, and we could add that case with Refaster.anyOf
. OTOH, perhaps we should separately flag Flux.just(singleton)
passed to a Publisher
-accepting method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same applies to Flux.just, and we could add that case with Refaster.anyOf
Do you mean something rewriting this one: Mono.flatMapMany(x -> Flux.just(transformation(x)))
? We could do it with something like this:
Publisher<S> before(Mono<T> mono) {
return Refaster.anyOf(
mono.flatMap(x -> Mono.just(transformation(x))),
mono.flatMapMany(x -> Flux.just(transformation(x)))
);
}
But the above would in some situations change the type of the expression from Flux
to Mono
resulting in compilation errors. Is that something that we consider acceptable?
flag Flux.just(singleton) passed to a Publisher-accepting method
I've tried a bit to come up with a Refaster rule for this but couldn't. Is this something that can actually be done with Refaster or only with error-prone?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code your suggesting seems good!
But the above would in some situations change the type of the expression from Flux to Mono resulting in compilation errors. Is that something that we consider acceptable?
No this is generally a no-go. We only allow this if in ~99% of the cases this won't result in compilation errors..
flag Flux.just(singleton) passed to a Publisher-accepting method
To be honest, I fail to see how this would look like 😬.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the above would in some situations change the type of the expression from
Flux
toMono
resulting in compilation errors. Is that something that we consider acceptable?
Good point. Ideally we indeed avoid this, though the similarity between the Mono
and Flux
APIs might allow us to get away with this in many cases. If we want to avoid this we could instead have a separate rule that adds .flux()
. (And then have separate Refaster rules (or a check) which "pushes down"/"dissolves" the .flux()
where possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC we could have a rule to transform
mono.flatMapMany(x -> Mono.just(transformation(x)))
-> mono.map(x -> transformation(x)).flux()
and then another rule / pattern for cases similar to this (the push down):
mono.flux().map(x -> transformation(x))
-> mono.map(x -> transformation(x)).flux()
At some point we might be able to go back to a mono (e.g. mono.flux().last()
-> mono
) (the dissolve).
I have to think about how many such cases there are. On one hand not adding the .flux()
and generating a compile error might force someone to reevaluate if a flux is truly necessary and if so manually fix it by adding the .flux(). Just adding it automatically without properly optimizing it after might hide some things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it seems that the rule MonoFlatMapToFlux
does interfere with this rule (see build failure).
The mentioned rule would rewrite mono.flatMapMany(x -> Mono.just(transformation(x)))
-> mono.flatMap(x -> Mono.just(transformation(x))).flux()
Then this rule would rewrite.
mono.flatMap(x -> Mono.just(transformation(x))).flux()
-> mono.map(x -> transformation(x)).flux()
So we get there in the end. I've removed the flatMapMany
cases.
* Prefer {@link Mono#map(Function)} over alternatives that unnecessarily require an inner | ||
* subscription. | ||
*/ | ||
abstract static class MonoMap<T, S> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can have two similar methods for Flux#map
and Flux#mapNotNull
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it okay if it is in the same PR?
Also, as far as a can see there are many possibilities here.
Flux<S> before(Flux<T> flux) {
return Refaster.anyOf(
flux.flatMap(x -> Flux.just(transformation(x))),
flux.flatMap(x -> Mono.just(transformation(x))),
flux.concatMap(x -> Flux.just(transformation(x))),
flux.concatMap(x -> Mono.just(transformation(x)))
);
}
These are just a few but potentially we might want to include more (e.g. for switchMap
or concatMapDelayError
). Also each of the above has multiple overloads that would also be valid candidates. How do we usually deal with such cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it okay if it is in the same PR?
As the changes are very similar this is fine 👍🏻 .
How do we usually deal with such cases?
There are a few template collections where we list many variants in the Refaster.anyOf
. For example, see AssertJTemplates.AssertThatObjectEnumerableIsEmpty
. It depends on how many overloads there are, it could always be a BugPattern.
I'm not that good in Reactor that I can now say that we should and/or can fix all these cases and whether they are behavior preserving 🤔.
Still, by using multiple before templates (I assume that is required), we could potentially flag a nice amount of violations with this Refaster template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FluxFlatMapUsage
check introduced in #26 will disallow the two suggested flatMap
cases. It seems unlikely that users will use {Flux,Mono}.just
i.c.w. an explicit concurrency level. One could argue that similarly it's unlikely that we find such concatMap
cases, but I actually found a few. So yeah, let's do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some more cases (I've checked and I couldn't find any other rules that would disallow them)
flux.concatMap(x -> Mono.just(transformation(x)), prefetch),
flux.concatMap(x -> Flux.just(transformation(x)), prefetch),
flux.concatMapDelayError(x -> Mono.just(transformation(x))),
flux.concatMapDelayError(x -> Flux.just(transformation(x))),
Thanks @Stephan202 for the suggestions (both the explanation and the applying) :) |
1779980
to
85f05df
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased and added a commit. The build will now fail due to the extra templates without corresponding test cases. I.c.w. the fromSupplier
cases perhaps it does make sense to create BugChecker
instead.
(Sorry for the slow review!)
* Prefer {@link Mono#map(Function)} over alternatives that unnecessarily require an inner | ||
* subscription. | ||
*/ | ||
abstract static class MonoMap<T, S> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FluxFlatMapUsage
check introduced in #26 will disallow the two suggested flatMap
cases. It seems unlikely that users will use {Flux,Mono}.just
i.c.w. an explicit concurrency level. One could argue that similarly it's unlikely that we find such concatMap
cases, but I actually found a few. So yeah, let's do this.
|
||
@BeforeTemplate | ||
Mono<S> before(Mono<T> mono) { | ||
return mono.flatMap(x -> Mono.just(transformation(x))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the above would in some situations change the type of the expression from
Flux
toMono
resulting in compilation errors. Is that something that we consider acceptable?
Good point. Ideally we indeed avoid this, though the similarity between the Mono
and Flux
APIs might allow us to get away with this in many cases. If we want to avoid this we could instead have a separate rule that adds .flux()
. (And then have separate Refaster rules (or a check) which "pushes down"/"dissolves" the .flux()
where possible.
Pushed a suggestion to |
Could you detail an example regarding |
No worries! There's no urgency 😄. I'll add a re-review of this PR to my TODO list (might take a bit, though 🤞).
Hah, it also took me a while to remember what I meant here 😅 . So I think what I meant is that expression such as |
Aah, that makes sense. Don't think I've ever saw that but might be worth covering it |
f82e8f0
to
39bd678
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased and added a commit to cover the Mono.fromSupplier
case. We should certainly start looking into a "smarter" approach, but this is a good step forward, so let's go! 🚀
Suggested commit message:
Introduce `{Mono,Flux}Map{,NotNull}` Refaster rules (#142)
Thanks @Stephan202 for the changes! |
39bd678
to
456cae9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really curious to see how this will change things downstream.
Thanks for another Reactor PR @cernat-catalin 🚀 !
mono.map
over mono.flatMap(x -> Mono.just())
{Mono,Flux}Map{,NotNull}
Refaster rules
Based on a suggestion from here.
Adds a Refaster template to prefer
mono.map(x -> valueTransformation(x))
over themono.flatMap(x -> Mono.just(valueTransformation(x)))
This avoids an unnecessary inner inner subscription.