CSharpFunctionalExtensions
CSharpFunctionalExtensions copied to clipboard
API review
I wanted to review the Result
API before moving ahead further with #148 and #151 - this gives a chance to plan any further refactoring in one place, so we have a better sense of the overall direction. Hopefully, this can serve as a starting point for some documentation too. For now, rather than fleshing this out with comprehensive descriptions, I am just going to add links to open issues and my ideas for improvements, highlighted in bold.
Updated 10 Mar 2020
Properties
Name | Type | Description |
---|---|---|
IsFailure | bool |
Whether the result is a failure. |
IsSuccess | bool |
Whether the result is a success. |
Error | string or E |
The error for a failure result. Accessing this property throws an exception if the result is a success. Use Result<T, E> to supply custom error types. |
Value | T |
The value for a success result. Accessing this property throws an exception if the result is a failure. |
Static result creation methods
Name | Description |
---|---|
Combine() | Combines several results into a single result. The returned result will be a failure if any of the input results are failures. |
Failure() | Creates a failure result. |
FailureIf(bool) | Creates a result whose success/failure reflects the supplied condition. Opposite of SuccessIf() . |
FirstFailureOrSuccess() | Returns the first failure from the supplied results. Returns a new success result if there is no failure. |
Success() | Creates a success result. |
SuccessIf(bool) | Creates a result whose success/failure reflects the supplied condition. Opposite of FailureIf() . |
Try() | Attempts to execute the supplied action. Returns a Result indicating whether the action executed successfully. |
Instance methods
Name | Description |
---|---|
ConvertFailure<K>() | Throws if the result is a success. Else returns a new failure result of the given type. See #136 and #148. |
Extension methods
Name | Description |
---|---|
Bind() BindWithTransactionScope() | |
Combine() | |
Deconstruct() | |
Ensure(bool predicate) | Returns a new failure result if the predicate is false. Otherwise returns the starting result. |
Finally(Func) | Passes the result to the given function (regardless of success/failure state) to yield a final output value. |
Map() MapWithTransactionScope() |
Creates a new result from the return value of a given function. If the calling Result is a failure, a new failure result is returned instead. |
Match() | |
OnFailure() | See #148 |
OnFailureCompensate() | See #148 |
OnSuccessTry() | TODO |
SelectMany() | |
Tap(Action action) | Executes the given action if the calling result is a success. Returns the calling result. |
TapIf(bool condition) | If the condition is true, calls Tap() . |
Static configuration fields
Name | Description |
---|---|
ErrorMessagesSeparator | A string that is used to separate any error messages concatenated by Combine() |
DefaultConfigureAwait | Default value for the continueOnCapturedContext parameter of the ConfigureAwait() method, which is called on every awaited Task . |
Great overview. I like Result.SuccessIf(bool)
better than Result.CreateSuccessIf(bool)
. And just as a reminder - we need to keep the old Ok
etc under the Obsolete
status.
I'm also thinking that maybe Result.Ok()
should be left without the Obsolete
attribute. Ok
is too wide-spread and intuitive to get rid of it for the sake of consistency. I don't have a strong opinion here, though.
echoing @vkhorikov very good proposal. my vote on static result creation methods is taking the first approach. but i think moving from struct to class and the loss of non-nullability is a major issue. isn't it one of the pillars of this library? struct also have the advantage of memory allocation, moving to classes might have impact on GC when dealing with lots of results.
Regarding structs vs classes. Can interfaces help?
Our teams use this library very heavily. Our vote would be
Result.Create(bool) => Result.SuccessIf(bool)
Result.CreateFailure(bool) => Result.FailIf(bool)
Result.Ok() => keep as it is and add Success()
Result.Fail() => keep as it is
I have pushed some commits and we now have:
Result.Create(bool) [Obsolete] => Result.SuccessIf(bool)
Result.CreateFailure(bool) [Obsolete] => Result.FailureIf(bool)
Result.Ok() => Result.Success()
Result.Fail() [Obsolete] => Result.Failure()
The reasons for Failure()
and not Fail()
are consistency and better semantics. "Fail" is a verb and therefore carries incorrect connotations of having some ("failing") effect on the Result
. Also, the failure/success flags are IsFailure
and IsSuccess
respectively. For this reason, Fail
is marked Obsolete
.
Ok()
is not marked Obsolete
for now, but we might reconsider this later.
Updated initial post