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

Introduce some Refaster rules that avoid nested Publishers #374

Merged
merged 2 commits into from
Nov 28, 2022

Conversation

werli
Copy link
Member

@werli werli commented Nov 26, 2022

Context

These proposed Refaster rules primarily aim to make Reactor code more readable as nested Publishers are avoided (e.g. Mono<Mono<String>>).

They may result in a minor performance improvement due to the dropped map() calls.

Summary

I've noticed an occurrence of the following code pattern:

Mono<String> method1() {
  Mono.just("foo")
    .map(str -> method2(str))
    .flatMap(identity());
}

Mono<String> method2(String str) {
  ...
} 

which these changes rewrite to:

Mono<String> method1() {
  Mono.just("foo")
    .flatMap(str -> method2(str));
}

Conceptionally, the same applies for:

  • Mono#flatMap(Function)
  • Mono#flatMapMap(Function)
  • Flux#concatMap(Function)
  • Flux#concatMap(Function, int)

and in theory also for some others (e.g. Flux#flatMap()), but these are already re-written by other rules IIUC.

Suggested commit message

Introduce some Refaster rules that avoid nested `Publisher`s (#374)

Comment on lines 638 to 670
@AfterTemplate
Flux<T> after(Mono<S> mono, Function<S, ? extends Publisher<T>> function) {
return mono.flatMapMany(function);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of function being of type Function<S, Mono<T>> first this rule and then MonoFlatMapToFlux are applied, which ultimately rewrite this code piece

mono.map(function).flatMapMany(identity());

to

mono.flatMap(function).flux();

/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class FlatMapManyIdentity<S, T> {
@BeforeTemplate
Flux<T> before(Mono<S> mono, Function<S, ? extends Publisher<T>> function) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admittedly, I couldn't figure out why

Suggested change
Flux<T> before(Mono<S> mono, Function<S, ? extends Publisher<T>> function) {
Flux<T> before(Mono<S> mono, Function<S, Publisher<T>> function) {

alone wouldn't do it. Anyone an idea what I'm missing here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latter case only function that return precisely a Publisher (so not a subtype) are matched.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Thanks a lot - clear.

Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a commit with some suggestions and also tweaked the suggested commit message; PTAL. :)

Tnx!

@@ -615,6 +615,58 @@ Flux<S> after(Flux<T> flux) {
}
}

/** Prefer {@link Mono#flatMap(Function)} over more contrived alternatives. */
static final class MonoFlatMapIdentity<S, T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally name the patterns after the @AfterTemplate. So this would be MonoFlatMap.

Copy link
Member Author

@werli werli Nov 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get this pattern beforehand - noted 📝

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries; we plan to automate this 😄

}

/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class FlatMapManyIdentity<S, T> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W.r.t. the use of generic type parameters: further up we use T and S the other way around... perhaps something we should try to "auto-fix" somehow.

/** Prefer {@link Mono#flatMapMany(Function)} over more contrived alternatives. */
static final class FlatMapManyIdentity<S, T> {
@BeforeTemplate
Flux<T> before(Mono<S> mono, Function<S, ? extends Publisher<T>> function) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latter case only function that return precisely a Publisher (so not a subtype) are matched.

/** Prefer {@link Mono#flatMap(Function)} over more contrived alternatives. */
static final class MonoFlatMapIdentity<S, T> {
@BeforeTemplate
Mono<T> before(Mono<S> mono, Function<S, Mono<T>> function) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches more, and is consistent with the relevant parameter types:

Suggested change
Mono<T> before(Mono<S> mono, Function<S, Mono<T>> function) {
Mono<T> before(Mono<S> mono, Function<? super S, ? extends Mono<? extends T>> function) {

Comment on lines 207 to 211
ImmutableSet<Flux<String>> testFlatMapManyIdentity() {
return ImmutableSet.of(
Mono.just("foo").map(Mono::just).flatMapMany(identity()),
Mono.just("foo").map(Flux::just).flatMapMany(identity()));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One test suffices for this case :)

Comment on lines 644 to 655
/** Prefer {@link Flux#concatMap(Function)} over more contrived alternatives. */
static final class ConcatMapIdentity<S, T> {
@BeforeTemplate
Flux<T> before(Flux<S> flux, Function<S, ? extends Publisher<T>> function) {
return flux.map(function).concatMap(identity());
}

@AfterTemplate
Flux<T> after(Flux<S> flux, Function<S, ? extends Publisher<T>> function) {
return flux.concatMap(function);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one can be merged with the existing FluxConcatMap rule. Likewise for the one below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx - I didn't get this apparently.

@werli
Copy link
Member Author

werli commented Nov 27, 2022

Thx @Stephan202 for the good comments. Updated commit message LGTM, that's closer to what I had in mind anyways.

@rickie rickie self-requested a review November 28, 2022 12:16
@rickie rickie force-pushed the werli/reactor-publisher-identity branch from 65c7672 to 193979f Compare November 28, 2022 12:27
Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice @werli , you are on a roll! Keep it going 🚀 !

Suggested commit message LGTM!

(Rebased, will merge once 🍏)

@rickie rickie added this to the 0.6.0 milestone Nov 28, 2022
@werli werli changed the title Extend Reactor identity Refaster rules Introduce some Refaster rules that avoid nested Publishers Nov 28, 2022
@rickie rickie merged commit f46859a into master Nov 28, 2022
@rickie rickie deleted the werli/reactor-publisher-identity branch November 28, 2022 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants