From cc1691e41ad1f11654c7727f056411beee052081 Mon Sep 17 00:00:00 2001 From: Jack Edwards Date: Fri, 10 Jan 2025 10:43:07 -0600 Subject: [PATCH 1/3] Add support for IEnumerable> ranged variable --- .../QueryTests/IEnumerableSelectTests.cs | 46 +++++++++++++++++++ .../Either/EitherEnumerableExtensions.cs | 20 ++++++++ 2 files changed, 66 insertions(+) create mode 100644 EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs create mode 100644 EasyMonads/Either/EitherEnumerableExtensions.cs diff --git a/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs new file mode 100644 index 0000000..48afef0 --- /dev/null +++ b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; + +namespace EasyMonads.Test.EitherTests.QueryTests +{ + [TestFixture] + internal class IEnumerableSelectTests + { + [Test] + public void Select_Works_For_IEnumerable_Either() + { + Dictionary resultDict = new Dictionary + { + { 0, "0" }, + { 1, "1" }, + { 2, "2" } + }; + + IEnumerable> results = from number in GetRange() + from text in ConvertToString(number) + select text; + + foreach (var result in results.Select((x, i) => new { Value = x, Index = i })) + { + result.Value.DoRight(x => Assert.AreEqual(resultDict[result.Index], x)); + result.Value.DoLeftOrNeither(Assert.Fail); + } + + return; + + IEnumerable> GetRange() + { + foreach (var entry in resultDict) + { + yield return entry.Key; + } + } + + Either ConvertToString(int number) + { + return number.ToString(); + } + } + } +} \ No newline at end of file diff --git a/EasyMonads/Either/EitherEnumerableExtensions.cs b/EasyMonads/Either/EitherEnumerableExtensions.cs new file mode 100644 index 0000000..f99b15d --- /dev/null +++ b/EasyMonads/Either/EitherEnumerableExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace EasyMonads +{ + public static class EitherEnumerableExtensions + { + public static IEnumerable> Select(this IEnumerable> enumerableEither, Func map) + { + return enumerableEither.Select(either => either.Map(map)); + } + + public static IEnumerable> SelectMany(this IEnumerable> enumerableEither, Func> bind, Func project) + { + return enumerableEither.Select, Either>(either => either.Bind(right => + bind(right).Bind(intermediate => project(right, intermediate)))); + } + } +} \ No newline at end of file From cc3e47259a2ec1f6fa927de7c8b6db654d50ead4 Mon Sep 17 00:00:00 2001 From: Jack Edwards Date: Fri, 10 Jan 2025 13:35:59 -0600 Subject: [PATCH 2/3] Add range support for IEnumerable>> --- .../QueryTests/IEnumerableSelectTests.cs | 37 +++++++++++++++++++ .../Either/EitherEnumerableExtensions.cs | 19 ++++++++++ 2 files changed, 56 insertions(+) diff --git a/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs index 48afef0..1380e92 100644 --- a/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs +++ b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NUnit.Framework; namespace EasyMonads.Test.EitherTests.QueryTests @@ -42,5 +43,41 @@ Either ConvertToString(int number) return number.ToString(); } } + + [Test] + public async Task Select_Works_For_Async_IEnumerable_Either() + { + Dictionary resultDict = new Dictionary + { + { 0, "0" }, + { 1, "1" }, + { 2, "2" } + }; + + IEnumerable> results = await from number in GetRangeAsync() + from text in ConvertToStringAsync(number) + select text; + + foreach (var result in results.Select((x, i) => new { Value = x, Index = i })) + { + result.Value.DoRight(x => Assert.AreEqual(resultDict[result.Index], x)); + result.Value.DoLeftOrNeither(Assert.Fail); + } + + return; + + IEnumerable>> GetRangeAsync() + { + foreach (var entry in resultDict) + { + yield return Either.FromRight(entry.Key).AsTask(); + } + } + + Task> ConvertToStringAsync(int number) + { + return Either.From(number.ToString()).AsTask(); + } + } } } \ No newline at end of file diff --git a/EasyMonads/Either/EitherEnumerableExtensions.cs b/EasyMonads/Either/EitherEnumerableExtensions.cs index f99b15d..8402b3b 100644 --- a/EasyMonads/Either/EitherEnumerableExtensions.cs +++ b/EasyMonads/Either/EitherEnumerableExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace EasyMonads { @@ -16,5 +17,23 @@ public static IEnumerable> SelectMany, Either>(either => either.Bind(right => bind(right).Bind(intermediate => project(right, intermediate)))); } + + public static IEnumerable>> Select(this IEnumerable>> enumerableEither, Func map) + { + return enumerableEither.Select(async eitherTask => await eitherTask.MatchAsync( + left: Either.FromLeft, + right: right => map(right), + neither: Either.Neither)); + } + + public static IEnumerable>> SelectMany(this IEnumerable>> enumerableEitherTasks, Func>> bind, Func project) + { + return enumerableEitherTasks.Select(async eitherTask => await eitherTask.BindAsync(async right => + await bind(right).BindAsync(delegate (TIntermediate intermediate) + { + Either projection = project(right, intermediate); + return projection.AsTask(); + }))); + } } } \ No newline at end of file From 9498d70354dc0a1750b3839a4e3de34013272ae6 Mon Sep 17 00:00:00 2001 From: Jack Edwards Date: Fri, 10 Jan 2025 17:20:00 -0600 Subject: [PATCH 3/3] Playing around --- .../QueryTests/IEnumerableSelectTests.cs | 18 ++-- EasyMonads/Either/EitherAsyncExtensions.cs | 4 +- .../Either/EitherEnumerableExtensions.cs | 86 ++++++++++++++++--- 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs index 1380e92..1599f32 100644 --- a/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs +++ b/EasyMonads.Test/EitherTests/QueryTests/IEnumerableSelectTests.cs @@ -54,10 +54,11 @@ public async Task Select_Works_For_Async_IEnumerable_Either() { 2, "2" } }; - IEnumerable> results = await from number in GetRangeAsync() - from text in ConvertToStringAsync(number) + IEnumerable> results = await + from number in GetRangeAsync() + from text in ConvertToString(number) select text; - + foreach (var result in results.Select((x, i) => new { Value = x, Index = i })) { result.Value.DoRight(x => Assert.AreEqual(resultDict[result.Index], x)); @@ -66,17 +67,14 @@ from text in ConvertToStringAsync(number) return; - IEnumerable>> GetRangeAsync() + Task>> GetRangeAsync() { - foreach (var entry in resultDict) - { - yield return Either.FromRight(entry.Key).AsTask(); - } + return resultDict.Select(x => Either.FromRight(x.Key)).AsTask(); } - Task> ConvertToStringAsync(int number) + Either ConvertToString(int number) { - return Either.From(number.ToString()).AsTask(); + return Either.From(number.ToString()); } } } diff --git a/EasyMonads/Either/EitherAsyncExtensions.cs b/EasyMonads/Either/EitherAsyncExtensions.cs index 2db978f..8226c90 100644 --- a/EasyMonads/Either/EitherAsyncExtensions.cs +++ b/EasyMonads/Either/EitherAsyncExtensions.cs @@ -149,7 +149,9 @@ public static async Task> Select( neither: Either.Neither); } - public static async Task> SelectMany(this Task> either, Func>> bind, Func project) + public static async Task> SelectMany(this Task> either, + Func>> bind, + Func project) { return await either.BindAsync(async right => await bind(right).BindAsync(delegate (TIntermediate intermediate) diff --git a/EasyMonads/Either/EitherEnumerableExtensions.cs b/EasyMonads/Either/EitherEnumerableExtensions.cs index 8402b3b..45f78cc 100644 --- a/EasyMonads/Either/EitherEnumerableExtensions.cs +++ b/EasyMonads/Either/EitherEnumerableExtensions.cs @@ -17,23 +17,89 @@ public static IEnumerable> SelectMany, Either>(either => either.Bind(right => bind(right).Bind(intermediate => project(right, intermediate)))); } - - public static IEnumerable>> Select(this IEnumerable>> enumerableEither, Func map) + + public static IEnumerable>> SelectMany(this IEnumerable>> enumerableAsyncEither, + Func>> bind, Func project) + { + return enumerableAsyncEither.Select(async either => await either.BindAsync(right => + bind(right).BindAsync(intermediate => project(right, intermediate)))); + } + + /* + public static async IAsyncEnumerable> SelectMany( + this IAsyncEnumerable> enumerableEither, + Func, IAsyncEnumerable>> bindAsync, + Func, Either, Either> project) { - return enumerableEither.Select(async eitherTask => await eitherTask.MatchAsync( - left: Either.FromLeft, - right: right => map(right), - neither: Either.Neither)); + await foreach (Either either in enumerableEither) + { + await foreach (Either intermediate in bindAsync(either)) + { + yield return project(either, intermediate); + } + } + } + + public static async IAsyncEnumerable> SelectMany( + this IAsyncEnumerable> enumerableEither, + Func> bind, + Func project) + { + await foreach (var either in enumerableEither) + { + yield return await either.BindAsync(async right => bind(right).Bind(delegate(TIntermediate intermediate) + { + Either projection = project(right, intermediate); + return projection; + })); + } } - public static IEnumerable>> SelectMany(this IEnumerable>> enumerableEitherTasks, Func>> bind, Func project) + public static async IAsyncEnumerable> SelectMany( + this IAsyncEnumerable> enumerableEither, + Func> bind, + Func project) { - return enumerableEitherTasks.Select(async eitherTask => await eitherTask.BindAsync(async right => - await bind(right).BindAsync(delegate (TIntermediate intermediate) + await foreach (var either in enumerableEither) + { + yield return await either.BindAsync(right => bind(right).BindAsync(delegate(TIntermediate intermediate) { Either projection = project(right, intermediate); return projection.AsTask(); - }))); + })); + } + } + */ + + public static async Task>> Select(this Task>> asyncEnumerableEither, Func map) + { + return (await asyncEnumerableEither).Select(map); + } + + public static async Task>> SelectMany(this Task>> asyncEnumerableEither, + Func>>> bind, + Func>, TResult> project) + { + return (await asyncEnumerableEither).Select(either => + { + return either.Bind(right => + bind(right).Bind(delegate(Func> intermediate) + { + Either projection = project(right, intermediate); + return projection; + })); + }); + } + + public static IEnumerable>> SelectMany(this IEnumerable>> asyncEnumerableEither, + Func> bind, + Func project) + { + return asyncEnumerableEither.Select(asyncEither => asyncEither.BindAsync(right => bind(right).Bind(delegate(TIntermediate intermediate) + { + Either projection = project(right, intermediate); + return projection; + }))); } } } \ No newline at end of file