Skip to content

Commit

Permalink
Merge pull request #735 from polyadic/inspect-left
Browse files Browse the repository at this point in the history
  • Loading branch information
bash authored Jul 14, 2023
2 parents 15b9cc0 + 6c5c878 commit 8f54aea
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 5 deletions.
25 changes: 25 additions & 0 deletions Funcky.Test/Monads/EitherTest.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ public void InspectReturnsOriginalValue(Either<string, int> either)
Assert.Equal(either, either.Inspect(NoOperation));
}

[Fact]
public void InspectLeftDoesNothingWhenEitherIsRight()
{
var either = Either<string, int>.Right(10);
either.InspectLeft(_ => throw new XunitException("Side effect was unexpectedly called"));
}

[Fact]
public void InspectLeftCallsSideEffectWhenEitherIsLeft()
{
const string value = "foo";
var either = Either<string, int>.Left(value);

var sideEffect = Option<string>.None;
either.InspectLeft(v => sideEffect = v);
FunctionalAssert.Some(value, sideEffect);
}

[Theory]
[MemberData(nameof(LeftAndRight))]
public void InspectLeftReturnsOriginalValue(Either<string, int> either)
{
Assert.Equal(either, either.InspectLeft(NoOperation));
}

[Fact]
public void GivenARightCaseTheGetOrElseFuncIsNotExecuted()
{
Expand Down
37 changes: 37 additions & 0 deletions Funcky.Test/Monads/OptionTest.Convenience.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Xunit.Sdk;

namespace Funcky.Test.Monads;

public sealed partial class OptionTest
{
[Fact]
public void InspectNoneDoesNothingWhenOptionIsNone()
{
var option = Option.Some(10);
option.InspectNone(() => throw new XunitException("Side effect was unexpectedly called"));
}

[Fact]
public void InspectNoneCallsSideEffectWhenOptionIsNone()
{
var option = Option<int>.None;

var sideEffect = false;
option.InspectNone(() => sideEffect = true);
Assert.True(sideEffect);
}

[Theory]
[MemberData(nameof(SomeAndNone))]
public void InspectLeftReturnsOriginalValue(Option<int> option)
{
Assert.Equal(option, option.InspectNone(NoOperation));
}

public static TheoryData<Option<int>> SomeAndNone()
=> new()
{
Option<int>.None,
Option.Some(42),
};
}
31 changes: 31 additions & 0 deletions Funcky.Test/Monads/ResultTest.Convenience.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Xunit.Sdk;

namespace Funcky.Test.Monads;

public sealed partial class ResultTest
{
[Fact]
public void InspectErrorDoesNothingWhenResultIsOk()
{
var result = Result.Ok("foo");
result.InspectError(_ => throw new XunitException("Side effect was unexpectedly called"));
}

[Fact]
public void InspectErrorCallsSideEffectWhenResultIsError()
{
var exception = new Exception("Bam!");
var result = Result<string>.Error(exception);

var sideEffect = Option<Exception>.None;
result.InspectError(v => sideEffect = v);
FunctionalAssert.Some(exception, sideEffect);
}

[Theory]
[MemberData(nameof(OkAndError))]
public void InspectErrorReturnsOriginalValue(Result<int> result)
{
Assert.Equal(result, result.InspectError(NoOperation));
}
}
10 changes: 5 additions & 5 deletions Funcky.Test/Monads/ResultTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,25 +176,25 @@ public void ErrorConstructorLeavesExistingStackTraceUnchanged()
{
var result = InterestingStackTrace(1);
var expectedStackTraceString = FunctionalAssert.Error(result).StackTrace;
_ = result.Match(ok: Result.Ok, error: Result<int>.Error);
_ = result.InspectError(error => Result<int>.Error(error));
var stackTraceString = FunctionalAssert.Error(result).StackTrace;
Assert.Equal(expectedStackTraceString, stackTraceString);
}

[Fact]
public void SelectManyWithOkResultMatchesTherightValue()
public void SelectManyWithOkResultMatchesTheRightValue()
=> FunctionalAssert.Ok(2, Result.Ok(1).SelectMany(i => Result.Ok(i + 1)));

[Fact]
public void SelectManyWithErrorResultMatchesTherightValue()
public void SelectManyWithErrorResultMatchesTheRightValue()
=> FunctionalAssert.Error(Result<int>.Error(new Exception("Any")).SelectMany(i => Result.Ok(i + 1)));

[Fact]
public void SelectManyReturnErrorResultWithOkResultMatchesTherightValue()
public void SelectManyReturnErrorResultWithOkResultMatchesTheRightValue()
=> FunctionalAssert.Error(Result.Ok(1).SelectMany(_ => Result<int>.Error(new Exception("Any"))));

[Fact]
public void SelectManyReturnErrorResultWithErrorResultMatchesTherightValue()
public void SelectManyReturnErrorResultWithErrorResultMatchesTheRightValue()
=> FunctionalAssert.Error(Result<int>.Error(new Exception("Any")).SelectMany(_ => Result<int>.Error(new Exception("Other"))));

[Fact]
Expand Down
7 changes: 7 additions & 0 deletions Funcky/Monads/Either/Either.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public Either<TLeft, TRight> OrElse(Either<TLeft, TRight> fallback)
public Either<TLeft, TRight> OrElse(Func<TLeft, Either<TLeft, TRight>> fallback)
=> Match(left: fallback, right: Either<TLeft>.Return);

/// <summary>Performs a side effect when the either is left and returns the either value again.</summary>
public Either<TLeft, TRight> InspectLeft(Action<TLeft> inspector)
{
Switch(left: inspector, right: NoOperation);
return this;
}

/// <remarks>Careful! This overload discards the left value.</remarks>
[Pure]
public TRight GetOrElse(TRight fallback)
Expand Down
9 changes: 9 additions & 0 deletions Funcky/Monads/Option/Option.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public Option<TItem> Inspect(Action<TItem> inspector)
return this;
}

/// <summary>
/// Performs a side effect when the option has no value (i.e. when the option is <see cref="None"/>) and returns the option again.
/// </summary>
public Option<TItem> InspectNone(Action inspector)
{
Switch(none: inspector, some: NoOperation);
return this;
}

/// <summary>
/// Returns an <see cref="IEnumerable{T}"/> that yields exactly one value when the option
/// has an item and nothing when the option is empty.
Expand Down
7 changes: 7 additions & 0 deletions Funcky/Monads/Result/Result.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public TValidResult GetOrElse(TValidResult fallback)
public TValidResult GetOrElse(Func<Exception, TValidResult> fallback)
=> Match(error: fallback, ok: Identity);

/// <summary>Performs a side effect when the result is error and returns the result again.</summary>
public Result<TValidResult> InspectError(Action<Exception> inspector)
{
Switch(ok: NoOperation, error: inspector);
return this;
}

public TValidResult GetOrThrow()
=> GetOrElse(ThrowWithOriginalStackTrace);

Expand Down
3 changes: 3 additions & 0 deletions Funcky/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#nullable enable
Funcky.Monads.Either<TLeft, TRight>.InspectLeft(System.Action<TLeft>! inspector) -> Funcky.Monads.Either<TLeft, TRight>
Funcky.Monads.Either<TLeft, TRight>.OrElse(Funcky.Monads.Either<TLeft, TRight> fallback) -> Funcky.Monads.Either<TLeft, TRight>
Funcky.Monads.Either<TLeft, TRight>.OrElse(System.Func<TLeft, Funcky.Monads.Either<TLeft, TRight>>! fallback) -> Funcky.Monads.Either<TLeft, TRight>
Funcky.Monads.Option<TItem>.InspectNone(System.Action! inspector) -> Funcky.Monads.Option<TItem>
Funcky.Monads.Result<TValidResult>.GetOrElse(System.Func<System.Exception!, TValidResult>! fallback) -> TValidResult
Funcky.Monads.Result<TValidResult>.GetOrElse(TValidResult fallback) -> TValidResult
Funcky.Monads.Result<TValidResult>.InspectError(System.Action<System.Exception!>! inspector) -> Funcky.Monads.Result<TValidResult>
Funcky.Monads.Result<TValidResult>.OrElse(Funcky.Monads.Result<TValidResult> fallback) -> Funcky.Monads.Result<TValidResult>
Funcky.Monads.Result<TValidResult>.OrElse(System.Func<System.Exception!, Funcky.Monads.Result<TValidResult>>! fallback) -> Funcky.Monads.Result<TValidResult>

0 comments on commit 8f54aea

Please sign in to comment.