diff --git a/Funcky.Test/Extensions/EnumerableExtensions/Flatten.cs b/Funcky.Test/Extensions/EnumerableExtensions/Flatten.cs new file mode 100644 index 000000000..11e902838 --- /dev/null +++ b/Funcky.Test/Extensions/EnumerableExtensions/Flatten.cs @@ -0,0 +1,11 @@ +namespace Funcky.Test.Extensions.EnumerableExtensions; + +public sealed class Flatten +{ + [Fact] + public void FlattenFlatsIEnumerable() + { + var elements = new List { 1, 2, 3 }; + Assert.Equal(elements, new List> { elements }.Flatten()); + } +} diff --git a/Funcky.Test/Monads/EitherTest.Flatten.cs b/Funcky.Test/Monads/EitherTest.Flatten.cs new file mode 100644 index 000000000..f3ac9e6e7 --- /dev/null +++ b/Funcky.Test/Monads/EitherTest.Flatten.cs @@ -0,0 +1,22 @@ +namespace Funcky.Test.Monads; + +public sealed partial class EitherTest +{ + [Fact] + public void FlattenLeftIsLeft() + { + Assert.Equal("test", FunctionalAssert.Left(Either>.Left("test").Flatten())); + } + + [Fact] + public void FlattenRightLeftIsLeft() + { + Assert.Equal("test", FunctionalAssert.Left(Either>.Right(Either.Left("test")).Flatten())); + } + + [Fact] + public void FlattenRightRightIsRight() + { + FunctionalAssert.Right(4711, Either>.Right(Either.Right(4711)).Flatten()); + } +} diff --git a/Funcky.Test/Monads/LazyTest.Flatten.cs b/Funcky.Test/Monads/LazyTest.Flatten.cs new file mode 100644 index 000000000..4842c099b --- /dev/null +++ b/Funcky.Test/Monads/LazyTest.Flatten.cs @@ -0,0 +1,12 @@ +using FsCheck; +using Funcky.FsCheck; +using Funcky.Test.TestUtils; + +namespace Funcky.Test.Monads; + +public sealed partial class LazyTest +{ + [FunckyProperty] + public Property FlattenLazyLazyIsLazy(Lazy input) + => CheckAssert.Equal(Lazy.Return(input).Flatten(), input); +} diff --git a/Funcky.Test/Monads/OptionTest.Flatten.cs b/Funcky.Test/Monads/OptionTest.Flatten.cs new file mode 100644 index 000000000..d2d3a2be0 --- /dev/null +++ b/Funcky.Test/Monads/OptionTest.Flatten.cs @@ -0,0 +1,22 @@ +namespace Funcky.Test.Monads; + +public sealed partial class OptionTest +{ + [Fact] + public void FlattenNoneIsNone() + { + FunctionalAssert.None(Option>.None.Flatten()); + } + + [Fact] + public void FlattenSomeNoneIsNone() + { + FunctionalAssert.None(Option.Some(Option.None).Flatten()); + } + + [Fact] + public void FlattenSomeSomeIsSome() + { + FunctionalAssert.Some(4711, Option.Some(Option.Some(4711)).Flatten()); + } +} diff --git a/Funcky.Test/Monads/ReaderTest.Flatten.cs b/Funcky.Test/Monads/ReaderTest.Flatten.cs new file mode 100644 index 000000000..2f8f7fa1f --- /dev/null +++ b/Funcky.Test/Monads/ReaderTest.Flatten.cs @@ -0,0 +1,12 @@ +using FsCheck; +using Funcky.FsCheck; +using Funcky.Test.TestUtils; + +namespace Funcky.Test.Monads; + +public sealed partial class ReaderTest +{ + [FunckyProperty] + public Property FlattenReaderReaderIsReader(string environment, Reader input) + => CheckAssert.Equal(input, Reader.Return(input).Flatten(), environment); +} diff --git a/Funcky.Test/Monads/ResultTest.Flatten.cs b/Funcky.Test/Monads/ResultTest.Flatten.cs new file mode 100644 index 000000000..aeb391b97 --- /dev/null +++ b/Funcky.Test/Monads/ResultTest.Flatten.cs @@ -0,0 +1,22 @@ +namespace Funcky.Test.Monads; + +public sealed partial class ResultTest +{ + [Fact] + public void FlattenErrorIsError() + { + FunctionalAssert.Error(Result>.Error(new Exception()).Flatten()); + } + + [Fact] + public void FlattenOkErrorIsError() + { + FunctionalAssert.Error(Result.Ok(Result.Error(new Exception())).Flatten()); + } + + [Fact] + public void FlattenOkOkIsOk() + { + FunctionalAssert.Ok(4711, Result.Ok(Result.Ok(4711)).Flatten()); + } +} diff --git a/Funcky/Extensions/EnumerableExtensions/Flatten.cs b/Funcky/Extensions/EnumerableExtensions/Flatten.cs new file mode 100644 index 000000000..c8b2b3463 --- /dev/null +++ b/Funcky/Extensions/EnumerableExtensions/Flatten.cs @@ -0,0 +1,7 @@ +namespace Funcky.Extensions; + +public static partial class EnumerableExtensions +{ + public static IEnumerable Flatten(this IEnumerable> enumerable) + => enumerable.SelectMany(Identity); +} diff --git a/Funcky/Monads/Either/EitherExtensions.cs b/Funcky/Monads/Either/EitherExtensions.cs index 126c2e119..55b13ad2f 100644 --- a/Funcky/Monads/Either/EitherExtensions.cs +++ b/Funcky/Monads/Either/EitherExtensions.cs @@ -2,6 +2,11 @@ namespace Funcky.Monads; public static partial class EitherExtensions { + public static Either Flatten(this Either> either) + where TLeft : notnull + where TRight : notnull + => either.SelectMany(Identity); + /// Returns the left value or if the is a right value. [Pure] public static Option LeftOrNone(this Either either) diff --git a/Funcky/Monads/Lazy/LazyExtensions.cs b/Funcky/Monads/Lazy/LazyExtensions.cs new file mode 100644 index 000000000..ffdfa59dd --- /dev/null +++ b/Funcky/Monads/Lazy/LazyExtensions.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; + +namespace Funcky.Monads; + +public static partial class LazyExtensions +{ + public static Lazy Flatten<[DynamicallyAccessedMembers(PublicParameterlessConstructor)] T>(this Lazy> lazy) + => Lazy.FromFunc(() => lazy.Value.Value); +} diff --git a/Funcky/Monads/Option/OptionExtensions.cs b/Funcky/Monads/Option/OptionExtensions.cs index 0e516c645..d6f3bc91a 100644 --- a/Funcky/Monads/Option/OptionExtensions.cs +++ b/Funcky/Monads/Option/OptionExtensions.cs @@ -4,6 +4,10 @@ namespace Funcky.Monads; public static partial class OptionExtensions { + public static Option Flatten(this Option> option) + where T : notnull + => option.SelectMany(Identity); + public static Either ToEither(this Option option, TLeft left) where TLeft : notnull where TRight : notnull diff --git a/Funcky/Monads/Reader/ReaderExtensions.cs b/Funcky/Monads/Reader/ReaderExtensions.cs new file mode 100644 index 000000000..2b3685a08 --- /dev/null +++ b/Funcky/Monads/Reader/ReaderExtensions.cs @@ -0,0 +1,9 @@ +namespace Funcky.Monads; + +public static partial class ReaderExtensions +{ + public static Reader Flatten(this Reader> reader) + where TEnvironment : notnull + where TItem : notnull + => reader.SelectMany(Identity); +} diff --git a/Funcky/Monads/Result/ResultExtensions.Traversable.cs b/Funcky/Monads/Result/ResultExtensions.Traversable.cs index 8c21d6ff5..70eed4b4d 100644 --- a/Funcky/Monads/Result/ResultExtensions.Traversable.cs +++ b/Funcky/Monads/Result/ResultExtensions.Traversable.cs @@ -3,7 +3,7 @@ namespace Funcky.Monads; -public static class ResultExtensions +public static partial class ResultExtensions { [Pure] public static Either> Traverse( diff --git a/Funcky/Monads/Result/ResultExtensions.cs b/Funcky/Monads/Result/ResultExtensions.cs new file mode 100644 index 000000000..b929c30c5 --- /dev/null +++ b/Funcky/Monads/Result/ResultExtensions.cs @@ -0,0 +1,8 @@ +namespace Funcky.Monads; + +public static partial class ResultExtensions +{ + public static Result Flatten(this Result> result) + where T : notnull + => result.SelectMany(Identity); +} diff --git a/Funcky/PublicAPI.Shipped.txt b/Funcky/PublicAPI.Shipped.txt index 2650399f2..c7b1e8bc0 100644 --- a/Funcky/PublicAPI.Shipped.txt +++ b/Funcky/PublicAPI.Shipped.txt @@ -262,6 +262,7 @@ static Funcky.Extensions.EnumerableExtensions.ElementAtOrNone(this Syst static Funcky.Extensions.EnumerableExtensions.ExclusiveScan(this System.Collections.Generic.IEnumerable! source, TAccumulate seed, System.Func! accumulator) -> System.Collections.Generic.IEnumerable! static Funcky.Extensions.EnumerableExtensions.FirstOrNone(this System.Collections.Generic.IEnumerable! source) -> Funcky.Monads.Option static Funcky.Extensions.EnumerableExtensions.FirstOrNone(this System.Collections.Generic.IEnumerable! source, System.Func! predicate) -> Funcky.Monads.Option +static Funcky.Extensions.EnumerableExtensions.Flatten(this System.Collections.Generic.IEnumerable!>! enumerable) -> System.Collections.Generic.IEnumerable! static Funcky.Extensions.EnumerableExtensions.ForEach(this System.Collections.Generic.IEnumerable! source, System.Action! action) -> Funcky.Unit static Funcky.Extensions.EnumerableExtensions.ForEach(this System.Collections.Generic.IEnumerable! source, System.Func! action) -> Funcky.Unit static Funcky.Extensions.EnumerableExtensions.GetNonEnumeratedCountOrNone(this System.Collections.Generic.IEnumerable! source) -> Funcky.Monads.Option @@ -758,11 +759,13 @@ static Funcky.Monads.EitherExtensions.Traverse(this Funcky.Mon static Funcky.Monads.EitherExtensions.Traverse(this Funcky.Monads.Either either, System.Func!>! selector) -> Funcky.Monads.Reader>! static Funcky.Monads.EitherExtensions.Traverse(this Funcky.Monads.Either either, System.Func>! selector) -> Funcky.Monads.Option> static Funcky.Monads.EitherExtensions.Traverse(this Funcky.Monads.Either either, System.Func>! selector) -> Funcky.Monads.Result> +static Funcky.Monads.EitherExtensions.Flatten(this Funcky.Monads.Either> either) -> Funcky.Monads.Either static Funcky.Monads.Lazy.FromFunc(System.Func! valueFactory) -> System.Lazy! static Funcky.Monads.Lazy.Return(T value) -> System.Lazy! static Funcky.Monads.LazyExtensions.Select(this System.Lazy! lazy, System.Func! selector) -> System.Lazy! static Funcky.Monads.LazyExtensions.SelectMany(this System.Lazy! lazy, System.Func!>! selector, System.Func! resultSelector) -> System.Lazy! static Funcky.Monads.LazyExtensions.SelectMany(this System.Lazy! lazy, System.Func!>! selector) -> System.Lazy! +static Funcky.Monads.LazyExtensions.Flatten(this System.Lazy!>! lazy) -> System.Lazy! static Funcky.Monads.Option.FromBoolean(bool boolean) -> Funcky.Monads.Option static Funcky.Monads.Option.FromBoolean(bool boolean, System.Func! selector) -> Funcky.Monads.Option static Funcky.Monads.Option.FromBoolean(bool boolean, TItem item) -> Funcky.Monads.Option @@ -797,12 +800,14 @@ static Funcky.Monads.OptionExtensions.Traverse(this Funcky.Monads.Opti static Funcky.Monads.OptionExtensions.Traverse(this Funcky.Monads.Option option, System.Func!>! selector) -> Funcky.Monads.Reader>! static Funcky.Monads.OptionExtensions.Traverse(this Funcky.Monads.Option option, System.Func>! selector) -> Funcky.Monads.Either> static Funcky.Monads.OptionExtensions.Traverse(this Funcky.Monads.Option option, System.Func>! selector) -> Funcky.Monads.Result> +static Funcky.Monads.OptionExtensions.Flatten(this Funcky.Monads.Option> option) -> Funcky.Monads.Option static Funcky.Monads.Reader.FromAction(System.Action! action) -> Funcky.Monads.Reader! static Funcky.Monads.Reader.FromFunc(System.Func! function) -> Funcky.Monads.Reader! static Funcky.Monads.Reader.Return(TResult value) -> Funcky.Monads.Reader! static Funcky.Monads.ReaderExtensions.Select(this Funcky.Monads.Reader! source, System.Func! selector) -> Funcky.Monads.Reader! static Funcky.Monads.ReaderExtensions.SelectMany(this Funcky.Monads.Reader! source, System.Func!>! selector, System.Func! resultSelector) -> Funcky.Monads.Reader! static Funcky.Monads.ReaderExtensions.SelectMany(this Funcky.Monads.Reader! source, System.Func!>! selector) -> Funcky.Monads.Reader! +static Funcky.Monads.ReaderExtensions.Flatten(this Funcky.Monads.Reader!>! reader) -> Funcky.Monads.Reader! static Funcky.Monads.Result.Ok(TValidResult result) -> Funcky.Monads.Result static Funcky.Monads.Result.Return(TValidResult result) -> Funcky.Monads.Result static Funcky.Monads.Result.Error(System.Exception! exception) -> Funcky.Monads.Result @@ -819,6 +824,7 @@ static Funcky.Monads.ResultExtensions.Traverse(this Funcky.Mona static Funcky.Monads.ResultExtensions.Traverse(this Funcky.Monads.Result result, System.Func!>! selector) -> Funcky.Monads.Reader>! static Funcky.Monads.ResultExtensions.Traverse(this Funcky.Monads.Result result, System.Func>! selector) -> Funcky.Monads.Option> static Funcky.Monads.ResultExtensions.Traverse(this Funcky.Monads.Result result, System.Func>! selector) -> Funcky.Monads.Either> +static Funcky.Monads.ResultExtensions.Flatten(this Funcky.Monads.Result> result) -> Funcky.Monads.Result static Funcky.Sequence.Concat(params System.Collections.Generic.IEnumerable![]! sources) -> System.Collections.Generic.IEnumerable! static Funcky.Sequence.Concat(System.Collections.Generic.IEnumerable!>! sources) -> System.Collections.Generic.IEnumerable! static Funcky.Sequence.Cycle(TResult element) -> System.Collections.Generic.IEnumerable!