-
Notifications
You must be signed in to change notification settings - Fork 51
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
Conversation
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
@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. |
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.
You mean Combined
?
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.
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. |
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.
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.
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.
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.
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.
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) { |
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.
Can we use Arrays.asList()
and delegate to the other ctor, to reduce code duplication?
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 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.
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.
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) { |
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.
Here I feel like we could have one overload delegate to the other via Arrays.asList()
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.
Yes, we could. Should we prefer the code reduction/deduplication over potential performance benefits?
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.
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) { |
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.
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)
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.
Ah, I see that we already have methods called combineFutures
that were added without me noticing :) So I guess there is some precedent
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.
Yes, perhaps those other ones were poorly named :(
all[i] = stages[i].toCompletableFuture(); | ||
} | ||
return CompletableFuture.allOf(all) | ||
.thenCompose(ignored -> function.apply(new Combined(stages))); |
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.
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.
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.
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));
}
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 then perhaps the entire method is not really worth including in the library at all..
8a32829
to
702f0dd
Compare
import java.util.List; | ||
import java.util.concurrent.CompletionStage; | ||
|
||
public class Combined { |
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.
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.
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.
Done!
No description provided.