-
Notifications
You must be signed in to change notification settings - Fork 251
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
feat: fluent use of Result
#1837
feat: fluent use of Result
#1837
Conversation
@ndr-brt still in draft, but since the issue (and preceding discussion) wasn't too firm on specifics, lets use this PR to align further. |
Result
Result
spi/core-spi/src/main/java/org/eclipse/dataspaceconnector/spi/result/Result.java
Fixed
Show fixed
Hide fixed
Codecov Report
@@ Coverage Diff @@
## main #1837 +/- ##
==========================================
+ Coverage 62.47% 62.53% +0.06%
==========================================
Files 779 779
Lines 16608 16632 +24
Branches 1085 1085
==========================================
+ Hits 10376 10401 +25
Misses 5781 5781
+ Partials 451 450 -1
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
/** | ||
* Executes a {@link Consumer} if this {@link Result} is successful | ||
*/ | ||
public void ifSuccess(Consumer<T> successAction) { |
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 would make this return this
as it could be used in a fluent context, e.g. to print a log and then the chain could continue, with a map
.
same for the ifFailure
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 ifSuccess
was intended to execute a conditional action, similar to Optional.ifPresent
and I would suggest keeping semantic consistency.
The whenSuccessful
is intended for usage in fluent blocks. If anything I would consolidate AbstractResult.whenSuccessul
and Result.whenSuccess
, keeping the latter.
Conversely, we can indeed return this
from the ifFailure
method, as it is semantically different from orElse
and could then be used as initial statement in a fluent block:
return result.ifFailure(...).mapTo(...);
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.
to be honest I have some opinions about the Optional
interface :)
the whenSuccessful
is different since the action get the whole result (that's probably not needed).
I would keep only one method that will execute an action on success and one that will do the same on failure.
Why do not call them onSuccess
and onFailure
?
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.
maybe we can even simplify it a bit:
- have one method each for success and failure (FWIW lets call them
onSuccess
andonFailure
, as you suggested), receiving either thecontent
or theFailure
, returningthis
- have one method called
flatMap
that accepts a mapping function and converts one Result into another. This would get rid of thewhenSuccess(ful)
stuff for now
that way, we can see if that fits the bill and extend later.
* @return {@link Result#failure(String)} if the Optional is empty, {@link Result#success(Object)} using the Optional's value otherwise. | ||
*/ | ||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") | ||
public static <T> Result<T> ofOptional(Optional<T> opt) { |
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.
fromOptional
could fit better?
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.
or just from
?
*/ | ||
public <R> Result<R> mapTo() { | ||
if (succeeded()) { | ||
return new Result<>(null, null); |
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 not sure about this, I'm wondering what could be the use of this function, mapping it to another type but with the content discarded... how this could be helpful?
Maybe only a mapToEmpty
/mapEmpty
that returns a Result<Void>
would be just enough.
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 think the more plausible case would be the failure situation, similar to:
public Result<Void> someMethod(){
Result<String> stringResult = getStringResult();
if(result.failed()){
return result.mapTo();
}
// ... other code
}
especially when the "other code" cannot use fluent statements
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't think of a case where I should map the result in a different way for the failure, it should be all part of the same chain.
If we want to have some code/validations to be executed only if the result is not failed we could introduce a compose
/flatMap
method that would be executed only if the result is successful
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 was referring to a situation where the other code
stuff is quite lengthy, and may involve calling methods, that do not return a Result
, but throw an exception, return a value or null, etc.
so the mapTo
is mainly used to convert a failed Result
into another Result
implicitly.
* | ||
* @return this | ||
*/ | ||
public AbstractResult<T, F> whenSuccessful(Consumer<AbstractResult<T, F>> content) { |
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.
This is the same as ifSuccess
, because this pass the whole result, but the failure will be always null as it is successful.
Same for orElseDo
, I would keep only the ifSuccess
/ifFailure
making them return this
for their fluent use.
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 problem with ifFailure
is a purely semantic one: we could then construct blocks like:
res.ifSuccess(...)
.ifFailure(...)
which (to my ears) sounds worse than
res.ifSuccess(...)
.orElse(...)
even if ifFailure
and orElse
perform the same function (maybe even call each other).
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.
they could be called onSuccess
and onFailure
* | ||
* @param exceptionSupplier provides an instance of the exception to throw | ||
*/ | ||
public <X extends Throwable> void orElseThrow(Supplier<? extends X> exceptionSupplier) throws 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.
It could return this
as well
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.
sounds legit, would only be useful in the non-throw case though.
9facf0f
to
c544881
Compare
spi/common/core-spi/src/main/java/org/eclipse/dataspaceconnector/spi/result/Result.java
Fixed
Show fixed
Hide fixed
ac7896f
to
345db9e
Compare
345db9e
to
1fa9d16
Compare
feat: add fluent syntax to result
What this PR changes/adds
adds some syntactic sugar to the
Result
andAbstractResult
class to make them usable in fluent statements.Why it does that
Enable code fluency and more descriptive and succinct code pieces.
Further notes
Linked Issue(s)
Closes #1823
Checklist
no-changelog
)