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

Implement combine with arbitrary number of stages #61

Merged
merged 3 commits into from
Mar 20, 2020

Conversation

spkrka
Copy link
Member

@spkrka spkrka commented Oct 15, 2019

No description provided.

@codecov
Copy link

codecov bot commented Mar 12, 2020

Codecov Report

Merging #61 into master will not change coverage by %.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##              master       #61   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
- Complexity        69        80   +11     
===========================================
  Files              6         7    +1     
  Lines            191       212   +21     
  Branches           9        14    +5     
===========================================
+ Hits             191       212   +21     
Impacted Files Coverage Δ Complexity Δ
...main/java/com/spotify/futures/CombinedFutures.java 100.00% <100.00%> (ø) 7.00 <7.00> (?)
...n/java/com/spotify/futures/CompletableFutures.java 100.00% <100.00%> (ø) 45.00 <4.00> (+4.00)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e4e1934...5b78ba1. Read the comment docs.

@spkrka
Copy link
Member Author

spkrka commented Mar 12, 2020

@mbruggmann @dflemstr - would this be ok to merge?

README.md Outdated

If you want to combine more than six futures of different types, use the other `combine` method.
Since it supports vararg usage, the function is now the first argument.
The `Combiner` object that is input to the function can be used to extract values from the input functions.
Copy link
Contributor

Choose a reason for hiding this comment

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

You mean Combined?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's a typo, will fix.

@@ -86,6 +86,26 @@ CompletableFutures.combineFutures(f1, f2, f3, f4, (a, b, c, d) -> completedFutur
CompletableFutures.combineFutures(f1, f2, f3, f4, f5, (a, b, c, d, e) -> completedFuture(a + b + c + d + e));
```

#### Combine an arbitrary number of futures

If you want to combine more than six futures of different types, use the other `combine` method.
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess one argument that could be made here is that if you need more than 6 futures, then it might be time to refactor the code instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, is something stopping us from simply adding more overloads? I think I would prefer having overloads with 6-31 parameters before resorting to varargs or similar.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, more than 6 futures may be a code smell in the general case. But if choosing between a generic "Combiner" object and adding 31 parameter methods, I'd prefer the combiner.

}
}

Combined(CompletionStage<?>[] stages) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use Arrays.asList() and delegate to the other ctor, to reduce code duplication?

Copy link
Member Author

Choose a reason for hiding this comment

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

We could do that, but the code duplication here is very small I think, and we might introduce some extra overhead by unnecessarily converting to a list.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can almost guarantee to you that the list will be optimized away through escape analysis in this case, and otherwise it is only an 8 byte cost

* @since 0.4.0
*/
public static <T> CompletionStage<T> combine(
Function<Combined, T> function, CompletionStage<?>... stages) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here I feel like we could have one overload delegate to the other via Arrays.asList()

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we could. Should we prefer the code reduction/deduplication over potential performance benefits?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, give me a benchmark if you want to prove me wrong :)

* @since 0.4.0
*/
public static <T> CompletionStage<T> combineFutures(
Function<Combined, CompletionStage<T>> function, CompletionStage<?>... stages) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm confused by the suffix -Futures here, what does it mean?

In the CompletionStage class, this is typically called a compose operation, so maybe combineCompose?

In other cases, this is typically called a flatMap operation, so maybe combineFlatMap? (I don't personally like that one)

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see that we already have methods called combineFutures that were added without me noticing :) So I guess there is some precedent

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, perhaps those other ones were poorly named :(

all[i] = stages[i].toCompletableFuture();
}
return CompletableFuture.allOf(all)
.thenCompose(ignored -> function.apply(new Combined(stages)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this line is the only difference, maybe we could break out common utility methods for the code above this line in all of the method overloads.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, yes. Perhaps should simply be implemented something like this:

 public static <T> CompletionStage<T> combineFutures(
          Function<Combined, CompletionStage<T>> function, CompletionStage<?>... stages) {
  return dereference(combine(function, stages));
}

Copy link
Member Author

Choose a reason for hiding this comment

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

But then perhaps the entire method is not really worth including in the library at all..

@spkrka spkrka force-pushed the krka/more_combine branch from 8a32829 to 702f0dd Compare March 13, 2020 15:42
import java.util.List;
import java.util.concurrent.CompletionStage;

public class Combined {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Consider naming this CombinedFutures or something to reduce the likelihood of collisions.

Also nit: Consider declaring this class as final since it is not meant to be extended.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done!

@spkrka spkrka requested a review from dflemstr March 16, 2020 08:27
@dflemstr dflemstr merged commit 25bbd6e into master Mar 20, 2020
@dflemstr dflemstr deleted the krka/more_combine branch March 20, 2020 17:01
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

Successfully merging this pull request may close these issues.

3 participants