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

Features/combine sequential #280

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class CombineMethodTests
[Fact]
public void Combine_combines_all_errors_together()
{
Result result1 = Result.Success();
Result<int> result1 = Result.Success(1);
Result result2 = Result.Failure("Failure 1");
Result result3 = Result.Failure("Failure 2");

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using FluentAssertions;

using Xunit;

using static CSharpFunctionalExtensions.Tests.ResultTests.CombineWithErrorMethodTests;

namespace CSharpFunctionalExtensions.Tests.ResultTests
{
public class CombineSequentialMethodTests
{
[Fact]
public async Task CombineSequential_execute_all_functions_when_all_are_success()
{
var firstReturnValue = 12;
var secondReturnValue = "value";
Task<Result<int>> FirstFunction() => Task.FromResult(Result.Success(firstReturnValue));

Task<Result<string>> SecondFunction() => Task.FromResult(Result.Success(secondReturnValue));

var result = await Result.CombineSequential(FirstFunction,
SecondFunction,
values => new
{
values.DataA,
values.DataB
});

result.IsSuccess.Should().BeTrue();
result.Value.DataA.Should().Be(firstReturnValue);
result.Value.DataB.Should().Be(secondReturnValue);
}
[Fact]
public async Task CombineSequential_execute_first_functions_when_it_fails()
{
var errorValue = "First function error";
Task<Result<int>> FirstFunction() => Task.FromResult(Result.Failure<int>(errorValue));

Task<Result<string>> SecondFunction() => Task.FromResult(Result.Success("value"));

var result = await Result.CombineSequential(FirstFunction,
SecondFunction,
Copy link
Owner

Choose a reason for hiding this comment

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

Minor (nit): the indentation is off.

values => new
{
values.DataA,
values.DataB
});
result.IsSuccess.Should().BeFalse();
result.Error.Should().Be(errorValue);
}
[Fact]
public async Task CombineSequential_execute_all_functions_when_second_fails()
{
var errorValue = "Second function error";

Task<Result<string>> FirstFunction() => Task.FromResult(Result.Success("value"));


Task<Result<int>> SecondFunction() => Task.FromResult(Result.Failure<int>(errorValue));


var result = await Result.CombineSequential(FirstFunction,
SecondFunction,
values => new
{
values.DataA,
values.DataB
});
result.IsSuccess.Should().BeFalse();
result.Error.Should().Be(errorValue);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FluentAssertions;

using Xunit;

namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions
Expand Down Expand Up @@ -49,7 +50,7 @@ public void CheckIf_T_executes_func_result_K_E_conditionally_and_returns_self(bo
actionExecuted.Should().Be(isSuccess && condition);
result.Should().Be(returned);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
Expand Down Expand Up @@ -79,7 +80,7 @@ public void CheckIf_T_executes_func_result_K_E_per_predicate_and_returns_self(bo
actionExecuted.Should().Be(isSuccess && condition);
result.Should().Be(returned);
}

[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>

</Project>
9 changes: 9 additions & 0 deletions CSharpFunctionalExtensions/Maybe/MaybeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ public static Maybe<T> Where<T>(this Maybe<T> maybe, Func<T, bool> predicate)

return Maybe<T>.None;
}
public static Result<K> Select<T, K>(this Result<T> maybe, Func<T, K> selector)
Copy link
Owner

Choose a reason for hiding this comment

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

Please don't add Select overloads, those should be either Map or Bind.

{
return maybe.Map(selector);
}

[Obsolete("Use Bind instead of this method")]
Copy link
Owner

Choose a reason for hiding this comment

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

Same here, no need to add new obsolete methods

public static Result<K> Select<T, K>(this Result<T> maybe, Func<T, Result<K>> selector)
{
return maybe.Bind(selector);
}
public static Maybe<K> Select<T, K>(this Maybe<T> maybe, Func<T, K> selector)
{
return maybe.Map(selector);
Expand Down
28 changes: 28 additions & 0 deletions CSharpFunctionalExtensions/Result/Methods/CombineSequential.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Threading.Tasks;

namespace CSharpFunctionalExtensions
{
public partial struct Result
{

/// <summary>
/// Combines several results (and any error messages) into a single result.
/// The returned result will be a failure if any of the input <paramref name="results"/> are failures.
/// Error message will have the first value of the failure operation
/// </summary>
/// <typeparam name="TReturnData">Type we will map</typeparam>
/// <typeparam name="TDataA">Type of the function <paramref name="a"/></typeparam>
/// <typeparam name="TDataB">>Type of the function <paramref name="b"/></typeparam>
/// <param name="a">First function that we want to execute</param>
/// <param name="b">Second function that we want to execute</param>
/// <param name="combineFunction">Functional to map all the result into <paramref name="TReturnData"/> </param>
/// <returns></returns>
public static async Task<Result<TReturnData>> CombineSequential<TReturnData, TDataA, TDataB>(
Func<Task<Result<TDataA>>> a,
Func<Task<Result<TDataB>>> b,
Func<(TDataA DataA, TDataB DataB), TReturnData> combineFunction)
=> await (await a())
.Bind(async resultA => (await b()).Map(resultB => combineFunction((resultA, resultB))));
Copy link
Owner

Choose a reason for hiding this comment

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

Such one-lines are hard to read. Please decompose into several lines, such as:

{
    var resultA = await a();
    var resultB = await b();
    // Bind, Map, etc
}

}
}