From a5fa258be6930d6f5df3552587313e36c5dadd2a Mon Sep 17 00:00:00 2001 From: Ruben Schmidmeister <4602612+bash@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:27:39 +0200 Subject: [PATCH] Add methods for inspecting the error case of alternative monads --- Funcky.Test/Monads/EitherTest.Convenience.cs | 25 +++++++++++++ Funcky.Test/Monads/OptionTest.Convenience.cs | 37 ++++++++++++++++++++ Funcky.Test/Monads/ResultTest.Convenience.cs | 31 ++++++++++++++++ Funcky/Monads/Either/Either.Convenience.cs | 7 ++++ Funcky/Monads/Option/Option.Convenience.cs | 9 +++++ Funcky/Monads/Result/Result.Convenience.cs | 7 ++++ Funcky/PublicAPI.Unshipped.txt | 3 ++ 7 files changed, 119 insertions(+) create mode 100644 Funcky.Test/Monads/OptionTest.Convenience.cs create mode 100644 Funcky.Test/Monads/ResultTest.Convenience.cs diff --git a/Funcky.Test/Monads/EitherTest.Convenience.cs b/Funcky.Test/Monads/EitherTest.Convenience.cs index ac7d978c..19c8bb4f 100644 --- a/Funcky.Test/Monads/EitherTest.Convenience.cs +++ b/Funcky.Test/Monads/EitherTest.Convenience.cs @@ -31,6 +31,31 @@ public void InspectReturnsOriginalValue(Either either) Assert.Equal(either, either.Inspect(NoOperation)); } + [Fact] + public void InspectLeftDoesNothingWhenEitherIsRight() + { + var either = Either.Right(10); + either.InspectLeft(_ => throw new XunitException("Side effect was unexpectedly called")); + } + + [Fact] + public void InspectLeftCallsSideEffectWhenEitherIsLeft() + { + const string value = "foo"; + var either = Either.Left(value); + + var sideEffect = Option.None; + either.InspectLeft(v => sideEffect = v); + FunctionalAssert.Some(value, sideEffect); + } + + [Theory] + [MemberData(nameof(LeftAndRight))] + public void InspectLeftReturnsOriginalValue(Either either) + { + Assert.Equal(either, either.InspectLeft(NoOperation)); + } + [Fact] public void GivenARightCaseTheGetOrElseFuncIsNotExecuted() { diff --git a/Funcky.Test/Monads/OptionTest.Convenience.cs b/Funcky.Test/Monads/OptionTest.Convenience.cs new file mode 100644 index 00000000..15349058 --- /dev/null +++ b/Funcky.Test/Monads/OptionTest.Convenience.cs @@ -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.None; + + var sideEffect = false; + option.InspectNone(() => sideEffect = true); + Assert.True(sideEffect); + } + + [Theory] + [MemberData(nameof(SomeAndNone))] + public void InspectLeftReturnsOriginalValue(Option option) + { + Assert.Equal(option, option.InspectNone(NoOperation)); + } + + public static TheoryData> SomeAndNone() + => new() + { + Option.None, + Option.Some(42), + }; +} diff --git a/Funcky.Test/Monads/ResultTest.Convenience.cs b/Funcky.Test/Monads/ResultTest.Convenience.cs new file mode 100644 index 00000000..abd5e7c0 --- /dev/null +++ b/Funcky.Test/Monads/ResultTest.Convenience.cs @@ -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.Error(exception); + + var sideEffect = Option.None; + result.InspectError(v => sideEffect = v); + FunctionalAssert.Some(exception, sideEffect); + } + + [Theory] + [MemberData(nameof(OkAndError))] + public void InspectErrorReturnsOriginalValue(Result result) + { + Assert.Equal(result, result.InspectError(NoOperation)); + } +} diff --git a/Funcky/Monads/Either/Either.Convenience.cs b/Funcky/Monads/Either/Either.Convenience.cs index 4d6b4421..2018a995 100644 --- a/Funcky/Monads/Either/Either.Convenience.cs +++ b/Funcky/Monads/Either/Either.Convenience.cs @@ -16,6 +16,13 @@ public Either Inspect(Action inspector) return this; } + /// Performs a side effect when the either is left and returns the either value again. + public Either InspectLeft(Action inspector) + { + Switch(left: inspector, right: NoOperation); + return this; + } + [Pure] public TRight GetOrElse(TRight fallback) => Match(left: _ => fallback, right: Identity); diff --git a/Funcky/Monads/Option/Option.Convenience.cs b/Funcky/Monads/Option/Option.Convenience.cs index 964ce492..80deeb59 100644 --- a/Funcky/Monads/Option/Option.Convenience.cs +++ b/Funcky/Monads/Option/Option.Convenience.cs @@ -51,6 +51,15 @@ public Option Inspect(Action inspector) return this; } + /// + /// Performs a side effect when the option has no value (i.e. when the option is ) and returns the option again. + /// + public Option InspectNone(Action inspector) + { + Switch(none: inspector, some: NoOperation); + return this; + } + /// /// Returns an that yields exactly one value when the option /// has an item and nothing when the option is empty. diff --git a/Funcky/Monads/Result/Result.Convenience.cs b/Funcky/Monads/Result/Result.Convenience.cs index ae984f6d..11a482c5 100644 --- a/Funcky/Monads/Result/Result.Convenience.cs +++ b/Funcky/Monads/Result/Result.Convenience.cs @@ -13,6 +13,13 @@ public Result Inspect(Action inspector) return this; } + /// Performs a side effect when the result is error and returns the result again. + public Result InspectError(Action inspector) + { + Switch(ok: NoOperation, error: inspector); + return this; + } + public TValidResult GetOrThrow() => Match( ok: Identity, diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 7dc5c581..245b08b6 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ #nullable enable +Funcky.Monads.Either.InspectLeft(System.Action! inspector) -> Funcky.Monads.Either +Funcky.Monads.Option.InspectNone(System.Action! inspector) -> Funcky.Monads.Option +Funcky.Monads.Result.InspectError(System.Action! inspector) -> Funcky.Monads.Result