-
Notifications
You must be signed in to change notification settings - Fork 310
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
With Extensions (Combine) #342
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions.With | ||
{ | ||
public class AsyncResultOfTETests | ||
{ | ||
[Fact] | ||
public async Task Combining_successful() | ||
{ | ||
var r1 = Result.Success<int, string>(1); | ||
var r2 = Result.Success<int, string>(2); | ||
var actual = await r1.WithMap(r2, (a, b) => Task.FromResult(a + b), (e1, e2) => "failure"); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
} | ||
|
||
[Fact] | ||
public async Task Successful_results_are_combined_with_binding() | ||
{ | ||
var r1 = Result.Success<int, string>(1); | ||
var r2 = Result.Success<int, string>(2); | ||
|
||
var actual = await r1 | ||
.WithBind(r2, (a, b) => Task.FromResult(Result.Success<int, string>(a + b)), (e1, e2) => "error"); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be(3); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions.With | ||
{ | ||
public class AsyncResultOfTTests | ||
{ | ||
[Fact] | ||
public async Task Successful_results_are_combined_with_mapping() | ||
{ | ||
var r1 = Result.Success(1); | ||
var r2 = Result.Success("hola"); | ||
|
||
var actual = await r1 | ||
.WithMap(r2, (a, b) => Task.FromResult(a + b)); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be("1hola"); | ||
} | ||
|
||
[Fact] | ||
public async Task Successful_results_are_combined_with_binding() | ||
{ | ||
var r1 = Result.Success(1); | ||
var r2 = Result.Success("hola"); | ||
|
||
var actual = await r1 | ||
.WithBind(r2, (a, b) => Task.FromResult(Result.Success(a + b))); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be("1hola"); | ||
} | ||
|
||
[Fact] | ||
public async Task Successful_results_are_combined_into_success() | ||
{ | ||
var r1 = Result.Success(1); | ||
var r2 = Result.Success("hola"); | ||
|
||
var actual = await r1 | ||
.WithBind(r2, (a, b) => Task.FromResult(Result.Success())); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
} | ||
|
||
[Fact] | ||
public async Task Failures_are_combined_into_failure() | ||
{ | ||
var r1 = Result.Failure<int>("Failed"); | ||
var r2 = Result.Success("hola"); | ||
|
||
var actual = await r1 | ||
.WithBind(r2, (a, b) => Task.FromResult(Result.Success())); | ||
|
||
actual.IsSuccess.Should().BeFalse(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions.With | ||
{ | ||
public class ResultOfTETests | ||
{ | ||
[Fact] | ||
public async void Successful_results_are_combined_with_map() | ||
{ | ||
var r1 = Result.Success<int, string>(1); | ||
var r2 = Result.Success<int, string>(2); | ||
|
||
var actual = r1.WithMap(r2, (a, b) => a + b, (e1, e2) => ""); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be(3); | ||
} | ||
|
||
[Fact] | ||
public async void Successful_results_are_combined_with_binding() | ||
{ | ||
var r1 = Result.Success<int, string>(1); | ||
var r2 = Result.Success<int, string>(2); | ||
|
||
var actual = r1.WithBind<int, string>(r2, (a, b) => Result.Success<int, string>(a + b), (e1, e2) => ""); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be(3); | ||
} | ||
|
||
[Fact] | ||
public async void Successful_results_are_combined_with_binding_different_types() | ||
{ | ||
var r1 = Result.Success<string, string>("Hi"); | ||
var r2 = Result.Success<int, string>(2); | ||
|
||
var actual = r1.WithBind(r2, (a, b) => Result.Success<string, string>(a + b), (e1, e2) => ""); | ||
|
||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be("Hi2"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions.With | ||
{ | ||
public class ResultOfTTest | ||
{ | ||
[Fact] | ||
public void Results_of_same_type_are_combined_correctly() | ||
{ | ||
var r1 = Result.Success("Hello"); | ||
var r2 = Result.Success("world"); | ||
|
||
var actual = r1.With(r2); | ||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be(("Hello", "world")); | ||
} | ||
|
||
[Fact] | ||
public void Results_of_different_type_are_mapped_correctly() | ||
{ | ||
var r1 = Result.Success("Hello"); | ||
var r2 = Result.Success("world"); | ||
|
||
var actual = r1.WithMap(r2, (a, b) => a + b); | ||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be("Helloworld"); | ||
} | ||
|
||
[Fact] | ||
public void Results_of_different_type_are_bound_correctly() | ||
{ | ||
var r1 = Result.Success("Hello"); | ||
var r2 = Result.Success("world"); | ||
|
||
var actual = r1.WithBind(r2, (a, b) => Result.Success(a + b)); | ||
actual.IsSuccess.Should().BeTrue(); | ||
actual.Value.Should().Be("Helloworld"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
|
||
namespace CSharpFunctionalExtensions | ||
{ | ||
public partial class ResultExtensions | ||
{ | ||
public static Result<T, E2> BindError<T, E, E2>(this Result<T, E> self, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so. OnFailureCompensate expects the same error type :) This maps from |
||
Func<E, Result<T, E2>> func) | ||
{ | ||
if (self.IsSuccess) | ||
{ | ||
return Result.Success<T, E2>(self.Value); | ||
} | ||
|
||
return func(self.Error); | ||
} | ||
|
||
public static Result<T> BindError<T>(this Result<T> self, | ||
Func<string, Result<T>> func) | ||
{ | ||
if (self.IsSuccess) | ||
{ | ||
return Result.Success(self.Value); | ||
} | ||
|
||
return func(self.Error); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpFunctionalExtensions | ||
{ | ||
public static partial class ResultExtensions | ||
{ | ||
public static Task<Result<TResult>> WithMap<T1, T2, TResult>(this Result<T1> a, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel it can be expressed via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're able to give an example, I'll be good with that :D |
||
Result<T2> b, | ||
Func<T1, T2, Task<TResult>> func) | ||
{ | ||
var mapSuccess = | ||
a.BindError(e1 => b | ||
.MapError(e2 => Errors.Join(e1, e2)) | ||
.Bind(_ => Result.Failure<T1>(e1))) | ||
.Bind(x => b | ||
.Map(y => func(x, y)) | ||
.MapError(el => el)); | ||
|
||
return mapSuccess; | ||
} | ||
|
||
public static Task<Result<TResult>> WithBind<T1, T2, TResult>(this Result<T1> a, | ||
Result<T2> b, | ||
Func<T1, T2, Task<Result<TResult>>> func) | ||
{ | ||
var mapSuccess = | ||
a.BindError(e1 => b | ||
.MapError(e2 => Errors.Join(e1, e2)) | ||
.Bind(_ => Result.Failure<T1>(e1))) | ||
.Bind(x => b | ||
.Bind(y => func(x, y)) | ||
.MapError(el => el)); | ||
|
||
return mapSuccess; | ||
} | ||
|
||
public static Task<Result> WithBind<T1, T2>(this Result<T1> a, | ||
Result<T2> b, | ||
Func<T1, T2, Task<Result>> map) | ||
{ | ||
var mapSuccess = | ||
a.BindError(el1 => b | ||
.MapError(el2 => string.Join(Result.ErrorMessagesSeparator, el1, el2)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
.Bind(_ => Result.Failure<T1>(el1))) | ||
.Bind(x => b | ||
.Bind(y => map(x, y)) | ||
.MapError(el => el)); | ||
|
||
return mapSuccess; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
namespace CSharpFunctionalExtensions | ||
{ | ||
public static partial class ResultExtensions | ||
{ | ||
public static Task<Result<TResult, E>> WithMap<T1, T2, E, TResult>(this Result<T1, E> a, | ||
Result<T2, E> b, | ||
Func<T1, T2, Task<TResult>> map, Func<E, E, E> combineError) | ||
{ | ||
var mapSuccess = | ||
a.BindError(e1 => b | ||
.MapError(e2 => combineError(e1, e2)) | ||
.Bind(_ => Result.Failure<T1, E>(e1))) | ||
.Bind(x => b | ||
.Map(y => map(x, y)) | ||
.MapError(el => el)); | ||
|
||
return mapSuccess; | ||
} | ||
|
||
public static Task<Result<TResult, E>> WithBind<T1, T2, E, TResult>(this Result<T1, E> a, | ||
Result<T2, E> b, | ||
Func<T1, T2, Task<Result<TResult, E>>> map, Func<E, E, E> combineError) | ||
{ | ||
var mapSuccess = | ||
a.BindError(e1 => b | ||
.MapError(e2 => combineError(e1, e2)) | ||
.Bind(_ => Result.Failure<T1, E>(e1))) | ||
.Bind(x => b | ||
.Bind(y => map(x, y)) | ||
.MapError(el => el)); | ||
|
||
return mapSuccess; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace CSharpFunctionalExtensions | ||
{ | ||
public static class Errors | ||
{ | ||
public static string Join(string e1, string e2) | ||
{ | ||
return string.Join(Result.ErrorMessagesSeparator, e1, e2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use string interpolation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be sensible to wait for #378 with the error combining |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System; | ||
|
||
namespace CSharpFunctionalExtensions | ||
{ | ||
public static partial class ResultExtensions | ||
{ | ||
public static Result<(T1, T2)> With<T1, T2>(this Result<T1> a, Result<T2> b) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Declare argument covariance |
||
{ | ||
return a.WithMap(b, (x, y) => (x, y)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
} | ||
|
||
public static Result<TResult> WithMap<T, K, TResult>( | ||
this Result<T> a, | ||
Result<K> b, | ||
Func<T, K, TResult> func) | ||
{ | ||
return a | ||
.BindError(e1 => b | ||
.MapError(e2 => Errors.Join(e1, e2)) | ||
.Bind(_ => Result.Failure<T>(e1)) | ||
).Bind(x => b | ||
.Map(y => func(x, y)) | ||
.MapError(e => e)); | ||
} | ||
|
||
public static Result<TResult> WithBind<T, K, TResult>( | ||
this Result<T> a, | ||
Result<K> b, | ||
Func<T, K, Result<TResult>> func) | ||
{ | ||
return a | ||
.BindError(e1 => b | ||
.MapError(e2 => Errors.Join(e1, e2)) | ||
.Bind(_ => Result.Failure<T>(e1)) | ||
).Bind(x => b | ||
.Bind(y => func(x, y)) | ||
.MapError(e => e)); | ||
} | ||
} | ||
} |
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.
System.ValueTuple
is shipped with the legacy framework starting from 4.7 and with netstandard starting from 2.0.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.
Comparing
TargetFramework
is considered leaky, for various good reasons, such as Mono RT, and "oh I missed a version", additionally we are not interested in theTargetFramework
at all, but in its features.Preprocessor directives express features.
Instead, consider comparing using
$(DefineConstants.Contains('NET5_0_OR_GREATER'))
which are supported by 3rd party runtimes and also well documented.