From 74238cc95ec7276eacce34e9afe2eeb06b5aab55 Mon Sep 17 00:00:00 2001 From: Tau <4602612+bash@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:09:04 +0100 Subject: [PATCH] Implement UpCast functions --- Funcky.Test/UpCastTest.cs | 60 ++++++++++++++++++++++++++++++++++ Funcky/PublicAPI.Unshipped.txt | 5 +++ Funcky/UpCast.cs | 54 ++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 Funcky.Test/UpCastTest.cs create mode 100644 Funcky/UpCast.cs diff --git a/Funcky.Test/UpCastTest.cs b/Funcky.Test/UpCastTest.cs new file mode 100644 index 00000000..3aca74f6 --- /dev/null +++ b/Funcky.Test/UpCastTest.cs @@ -0,0 +1,60 @@ +using System.Collections.Immutable; + +namespace Funcky.Test; + +public sealed class UpCastTest +{ + [Fact] + public void UpCastsReferenceTypeToObject() + { + const string arbitraryStringValue = "string"; + + Option option = UpCast.From(Option.Return(arbitraryStringValue)); + FunctionalAssert.Some(arbitraryStringValue, option); + + Either either = UpCast.From(Either.Return(arbitraryStringValue)); + FunctionalAssert.Right(arbitraryStringValue, either); + + Result result = UpCast.From(Result.Return(arbitraryStringValue)); + FunctionalAssert.Ok(arbitraryStringValue, result); + + Lazy lazy = UpCast.From(Lazy.Return(arbitraryStringValue)); + Assert.Equal(arbitraryStringValue, lazy.Value); + } + + [Fact] + public void UpCastsValueTypeToObject() + { + const int arbitraryIntegerValue = 10; + + Option option = UpCast.From(Option.Return(arbitraryIntegerValue)); + FunctionalAssert.Some(arbitraryIntegerValue, option); + + Either either = UpCast.From(Either.Return(arbitraryIntegerValue)); + FunctionalAssert.Right(arbitraryIntegerValue, either); + + Result result = UpCast.From(Result.Return(arbitraryIntegerValue)); + FunctionalAssert.Ok(arbitraryIntegerValue, result); + + Lazy lazy = UpCast.From(Lazy.Return(arbitraryIntegerValue)); + Assert.Equal(arbitraryIntegerValue, lazy.Value); + } + + [Fact] + public void UpCastsToInterface() + { + var arbitraryImplementationValue = ImmutableArray.Empty; + + Option> option = UpCast>.From(Option.Return(arbitraryImplementationValue)); + FunctionalAssert.Some(arbitraryImplementationValue, option); + + Either> either = UpCast>.From(Either.Return(arbitraryImplementationValue)); + FunctionalAssert.Right(arbitraryImplementationValue, either); + + Result> result = UpCast>.From(Result.Return(arbitraryImplementationValue)); + FunctionalAssert.Ok(arbitraryImplementationValue, result); + + Lazy> lazy = UpCast>.From(Lazy.Return(arbitraryImplementationValue)); + Assert.Equal(arbitraryImplementationValue, lazy.Value); + } +} diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 5a25287e..d75c8b9c 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -9,6 +9,7 @@ Funcky.Monads.Result.GetOrElse(TValidResult fallback) -> TValidRes Funcky.Monads.Result.InspectError(System.Action! inspector) -> Funcky.Monads.Result Funcky.Monads.Result.OrElse(Funcky.Monads.Result fallback) -> Funcky.Monads.Result Funcky.Monads.Result.OrElse(System.Func>! fallback) -> Funcky.Monads.Result +Funcky.UpCast static Funcky.Extensions.EnumeratorExtensions.MoveNextOrNone(this System.Collections.Generic.IEnumerator! enumerator) -> Funcky.Monads.Option static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this string! value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option @@ -16,3 +17,7 @@ static Funcky.Extensions.ParseExtensions.ParseOrNone(this string? va static Funcky.Extensions.ParseExtensions.ParseOrNone(this System.ReadOnlySpan value, System.IFormatProvider? provider) -> Funcky.Monads.Option static Funcky.Extensions.StringExtensions.Chunk(this string! source, int size) -> System.Collections.Generic.IEnumerable! static Funcky.Extensions.StringExtensions.SlidingWindow(this string! source, int width) -> System.Collections.Generic.IEnumerable! +static Funcky.UpCast.From(System.Lazy! lazy) -> System.Lazy! +static Funcky.UpCast.From(Funcky.Monads.Option option) -> Funcky.Monads.Option +static Funcky.UpCast.From(Funcky.Monads.Either either) -> Funcky.Monads.Either +static Funcky.UpCast.From(Funcky.Monads.Result result) -> Funcky.Monads.Result diff --git a/Funcky/UpCast.cs b/Funcky/UpCast.cs new file mode 100644 index 00000000..b78fd868 --- /dev/null +++ b/Funcky/UpCast.cs @@ -0,0 +1,54 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Funcky; + +/// Functions to upcast monad values. +public static class UpCast + where TResult : notnull +{ + /// Upcasts an 's item to . + /// + /// Upcasting an option's item from to : + /// result = UpCast.From(Option.Return("hello world")); + /// ]]> + public static Option From(Option option) + where TItem : TResult + => option.Select(From); + + /// Upcasts the right side of an to . + /// + /// Upcasting an either's right value from to : + /// result = UpCast.From(Either.Return("hello world")); + /// ]]> + public static Either From(Either either) + where TLeft : notnull + where TRight : TResult + => either.Select(From); + + /// Upcasts the success result of a to . + /// + /// Upcasting a result's success value from to : + /// result = UpCast.From(Result.Return("hello world")); + /// ]]> + public static Result From(Result result) + where TValidResult : TResult + => result.Select(From); + + /// Upcasts the value of a to . + /// + /// Upcasting a lazy's value from to : + /// result = UpCast.From(Lazy.Return("hello world")); + /// ]]> + [SuppressMessage("", "IL2091: DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter", Justification = "Public parameterless constructor is only used when a Lazy is created without providing a value or func.")] + public static Lazy From(Lazy lazy) + where T : TResult + => lazy.Select(From); + + private static TResult From(TValue value) + where TValue : TResult + => value; +}