Skip to content

Commit a1f80fd

Browse files
Merge pull request #866 from polyadic/add-flatten
Add Flatten to monads
2 parents 3e9ebca + b6bd6be commit a1f80fd

File tree

14 files changed

+151
-1
lines changed

14 files changed

+151
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Funcky.Test.Extensions.EnumerableExtensions;
2+
3+
public sealed class Flatten
4+
{
5+
[Fact]
6+
public void FlattenFlatsIEnumerable()
7+
{
8+
var elements = new List<int> { 1, 2, 3 };
9+
Assert.Equal(elements, new List<IEnumerable<int>> { elements }.Flatten());
10+
}
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Funcky.Test.Monads;
2+
3+
public sealed partial class EitherTest
4+
{
5+
[Fact]
6+
public void FlattenLeftIsLeft()
7+
{
8+
Assert.Equal("test", FunctionalAssert.Left(Either<string, Either<string, int>>.Left("test").Flatten()));
9+
}
10+
11+
[Fact]
12+
public void FlattenRightLeftIsLeft()
13+
{
14+
Assert.Equal("test", FunctionalAssert.Left(Either<string, Either<string, int>>.Right(Either<string, int>.Left("test")).Flatten()));
15+
}
16+
17+
[Fact]
18+
public void FlattenRightRightIsRight()
19+
{
20+
FunctionalAssert.Right(4711, Either<string, Either<string, int>>.Right(Either<string, int>.Right(4711)).Flatten());
21+
}
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using FsCheck;
2+
using Funcky.FsCheck;
3+
using Funcky.Test.TestUtils;
4+
5+
namespace Funcky.Test.Monads;
6+
7+
public sealed partial class LazyTest
8+
{
9+
[FunckyProperty]
10+
public Property FlattenLazyLazyIsLazy(Lazy<string> input)
11+
=> CheckAssert.Equal(Lazy.Return(input).Flatten(), input);
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Funcky.Test.Monads;
2+
3+
public sealed partial class OptionTest
4+
{
5+
[Fact]
6+
public void FlattenNoneIsNone()
7+
{
8+
FunctionalAssert.None(Option<Option<int>>.None.Flatten());
9+
}
10+
11+
[Fact]
12+
public void FlattenSomeNoneIsNone()
13+
{
14+
FunctionalAssert.None(Option.Some(Option<int>.None).Flatten());
15+
}
16+
17+
[Fact]
18+
public void FlattenSomeSomeIsSome()
19+
{
20+
FunctionalAssert.Some(4711, Option.Some(Option.Some(4711)).Flatten());
21+
}
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using FsCheck;
2+
using Funcky.FsCheck;
3+
using Funcky.Test.TestUtils;
4+
5+
namespace Funcky.Test.Monads;
6+
7+
public sealed partial class ReaderTest
8+
{
9+
[FunckyProperty]
10+
public Property FlattenReaderReaderIsReader(string environment, Reader<string, string> input)
11+
=> CheckAssert.Equal(input, Reader<string>.Return(input).Flatten(), environment);
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Funcky.Test.Monads;
2+
3+
public sealed partial class ResultTest
4+
{
5+
[Fact]
6+
public void FlattenErrorIsError()
7+
{
8+
FunctionalAssert.Error(Result<Result<int>>.Error(new Exception()).Flatten());
9+
}
10+
11+
[Fact]
12+
public void FlattenOkErrorIsError()
13+
{
14+
FunctionalAssert.Error(Result.Ok(Result<int>.Error(new Exception())).Flatten());
15+
}
16+
17+
[Fact]
18+
public void FlattenOkOkIsOk()
19+
{
20+
FunctionalAssert.Ok(4711, Result.Ok(Result.Ok(4711)).Flatten());
21+
}
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Funcky.Extensions;
2+
3+
public static partial class EnumerableExtensions
4+
{
5+
public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> enumerable)
6+
=> enumerable.SelectMany(Identity);
7+
}

Funcky/Monads/Either/EitherExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ namespace Funcky.Monads;
22

33
public static partial class EitherExtensions
44
{
5+
public static Either<TLeft, TRight> Flatten<TLeft, TRight>(this Either<TLeft, Either<TLeft, TRight>> either)
6+
where TLeft : notnull
7+
where TRight : notnull
8+
=> either.SelectMany(Identity);
9+
510
/// <summary>Returns the left value or <see cref="Option{TItem}.None()"/> if the <paramref name="either"/> is a right value.</summary>
611
[Pure]
712
public static Option<TLeft> LeftOrNone<TLeft, TRight>(this Either<TLeft, TRight> either)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes;
3+
4+
namespace Funcky.Monads;
5+
6+
public static partial class LazyExtensions
7+
{
8+
public static Lazy<T> Flatten<[DynamicallyAccessedMembers(PublicParameterlessConstructor)] T>(this Lazy<Lazy<T>> lazy)
9+
=> Lazy.FromFunc(() => lazy.Value.Value);
10+
}

Funcky/Monads/Option/OptionExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ namespace Funcky.Monads;
44

55
public static partial class OptionExtensions
66
{
7+
public static Option<T> Flatten<T>(this Option<Option<T>> option)
8+
where T : notnull
9+
=> option.SelectMany(Identity);
10+
711
public static Either<TLeft, TRight> ToEither<TLeft, TRight>(this Option<TRight> option, TLeft left)
812
where TLeft : notnull
913
where TRight : notnull

0 commit comments

Comments
 (0)