Skip to content

Commit

Permalink
Add methods for inspecting the error case of alternative monads
Browse files Browse the repository at this point in the history
  • Loading branch information
bash committed Jul 10, 2023
1 parent fd8ab52 commit a5fa258
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 0 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));
}
}
7 changes: 7 additions & 0 deletions Funcky/Monads/Either/Either.Convenience.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public Either<TLeft, TRight> Inspect(Action<TRight> inspector)
return this;
}

/// <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;
}

[Pure]
public TRight GetOrElse(TRight fallback)
=> Match(left: _ => fallback, right: Identity);
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 @@ -13,6 +13,13 @@ public Result<TValidResult> Inspect(Action<TValidResult> inspector)
return this;
}

/// <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()
=> Match(
ok: Identity,
Expand Down
3 changes: 3 additions & 0 deletions Funcky/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
#nullable enable
Funcky.Monads.Either<TLeft, TRight>.InspectLeft(System.Action<TLeft>! inspector) -> Funcky.Monads.Either<TLeft, TRight>
Funcky.Monads.Option<TItem>.InspectNone(System.Action! inspector) -> Funcky.Monads.Option<TItem>
Funcky.Monads.Result<TValidResult>.InspectError(System.Action<System.Exception!>! inspector) -> Funcky.Monads.Result<TValidResult>

0 comments on commit a5fa258

Please sign in to comment.