From 0c344a0446d0892b31eb27a3e0f9a3bf0b424b66 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Sun, 13 Nov 2022 09:32:57 +0100 Subject: [PATCH 1/7] Rewrite empty without CE --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index b94fbb26..d6cb2a30 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -11,10 +11,15 @@ module TaskSeq = // Just for convenience module Internal = TaskSeqInternal - let empty<'T> = taskSeq { - for c: 'T in [] do - yield c - } + let empty<'T> = + { new IAsyncEnumerable<'T> with + member _.GetAsyncEnumerator (_) = + { new IAsyncEnumerator<'T> with + member _.MoveNextAsync () = ValueTask.FromResult false + member _.get_Current () = invalidOp "The sequence is empty." + member _.DisposeAsync () = ValueTask.CompletedTask + } + } let isEmpty source = Internal.isEmpty source From 8df9f533cd7d7be357ee03bbfd638d29c5676ec0 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Sun, 13 Nov 2022 10:52:02 +0100 Subject: [PATCH 2/7] Align with tests expectations --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index d6cb2a30..2d32b4bb 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -16,7 +16,7 @@ module TaskSeq = member _.GetAsyncEnumerator (_) = { new IAsyncEnumerator<'T> with member _.MoveNextAsync () = ValueTask.FromResult false - member _.get_Current () = invalidOp "The sequence is empty." + member _.get_Current () = Unchecked.defaultof<'T> member _.DisposeAsync () = ValueTask.CompletedTask } } From d56de14f9b82074952c3eccf0ed0395e6e786156 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Sun, 13 Nov 2022 18:57:32 +0100 Subject: [PATCH 3/7] Update src/FSharp.Control.TaskSeq/TaskSeq.fs Co-authored-by: Abel Braaksma --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index 2d32b4bb..6e8dec95 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -16,7 +16,7 @@ module TaskSeq = member _.GetAsyncEnumerator (_) = { new IAsyncEnumerator<'T> with member _.MoveNextAsync () = ValueTask.FromResult false - member _.get_Current () = Unchecked.defaultof<'T> + member _.Current with get() = Unchecked.defaultof<'T> member _.DisposeAsync () = ValueTask.CompletedTask } } From a4eb7059403afe9f3c77a18957827cdeb6dbf479 Mon Sep 17 00:00:00 2001 From: Gustavo Leon <1261319+gusty@users.noreply.github.com> Date: Sun, 13 Nov 2022 18:57:59 +0100 Subject: [PATCH 4/7] Apply format Co-authored-by: Abel Braaksma --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index 6e8dec95..fc89fc2a 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -17,7 +17,7 @@ module TaskSeq = { new IAsyncEnumerator<'T> with member _.MoveNextAsync () = ValueTask.FromResult false member _.Current with get() = Unchecked.defaultof<'T> - member _.DisposeAsync () = ValueTask.CompletedTask + member _.DisposeAsync () = ValueTask.CompletedTask } } From 2745c970195ee856e9962d62db02a1ba5d9aff0f Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Thu, 24 Nov 2022 16:04:20 +0100 Subject: [PATCH 5/7] Move ValueTask constructors to more readable module extensions --- src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs | 11 ++++++----- src/FSharp.Control.TaskSeq/Utils.fs | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs index 8ec5cf34..2331fc28 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqBuilder.fs @@ -13,6 +13,7 @@ open System.Threading.Tasks.Sources open FSharp.Core.CompilerServices open FSharp.Core.CompilerServices.StateMachineHelpers +open FSharp.Control [] @@ -279,14 +280,14 @@ and [] TaskSeq<'Machine, 'T if this._machine.ResumptionPoint = -1 then // can't use as IAsyncEnumerator before IAsyncEnumerable logInfo "at MoveNextAsync: Resumption point = -1" - ValueTask() + ValueTask.False elif this._machine.Data.completed then logInfo "at MoveNextAsync: completed = true" // return False when beyond the last item this._machine.Data.promiseOfValueOrEnd.Reset() - ValueTask() + ValueTask.False else logInfo "at MoveNextAsync: normal resumption scenario" @@ -343,18 +344,18 @@ and [] TaskSeq<'Machine, 'T // the Current value data.current <- ValueNone - ValueTask(result) + ValueTask.FromResult result | ValueTaskSourceStatus.Faulted | ValueTaskSourceStatus.Canceled | ValueTaskSourceStatus.Pending as state -> logInfo ("at MoveNextAsyncResult: case ", state) - ValueTask(this, version) // uses IValueTaskSource<'T> + ValueTask.ofIValueTaskSource this version | _ -> logInfo "at MoveNextAsyncResult: Unexpected state" // assume it's a possibly new, not yet supported case, treat as default - ValueTask(this, version) // uses IValueTaskSource<'T> + ValueTask.ofIValueTaskSource this version and TaskSeqCode<'T> = ResumableCode, unit> and TaskSeqStateMachine<'T> = ResumableStateMachine> diff --git a/src/FSharp.Control.TaskSeq/Utils.fs b/src/FSharp.Control.TaskSeq/Utils.fs index 74bd115a..09d0e504 100644 --- a/src/FSharp.Control.TaskSeq/Utils.fs +++ b/src/FSharp.Control.TaskSeq/Utils.fs @@ -5,12 +5,26 @@ open System.Threading.Tasks [] module ValueTaskExtensions = /// Extensions for ValueTask that are not available in NetStandard 2.1, but are - /// available in .NET 5+. + /// available in .NET 5+. We put them in Extension space to mimic the behavior of NetStandard 2.1 type ValueTask with /// (Extension member) Gets a task that has already completed successfully. static member inline CompletedTask = Unchecked.defaultof + +module ValueTask = + /// A successfully completed ValueTask of boolean that has the value false. + let False = ValueTask() + + /// A successfully completed ValueTask of boolean that has the value true. + let True = ValueTask true + + /// Creates a ValueTask with the supplied result of the successful operation. + let inline FromResult (x: 'T) = ValueTask<'T> x + + /// Creates a ValueTask with an IValueTaskSource representing the operation + let inline ofIValueTaskSource taskSource version = ValueTask(taskSource, version) + module Task = /// Convert an Async<'T> into a Task<'T> let inline ofAsync (async: Async<'T>) = task { return! async } From 524c1f67e4a9516368cfec2726b42ca8d49a5261 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Thu, 24 Nov 2022 16:04:46 +0100 Subject: [PATCH 6/7] Fix compile errors in TaskSeq.empty --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index fc89fc2a..8e4ae80c 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -13,11 +13,11 @@ module TaskSeq = let empty<'T> = { new IAsyncEnumerable<'T> with - member _.GetAsyncEnumerator (_) = + member _.GetAsyncEnumerator(_) = { new IAsyncEnumerator<'T> with - member _.MoveNextAsync () = ValueTask.FromResult false - member _.Current with get() = Unchecked.defaultof<'T> - member _.DisposeAsync () = ValueTask.CompletedTask + member _.MoveNextAsync() = ValueTask.False + member _.Current = Unchecked.defaultof<'T> + member _.DisposeAsync() = ValueTask.CompletedTask } } From d68a88422316fe5a7a2e157d580fdd40e31843b9 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Thu, 24 Nov 2022 18:54:06 +0100 Subject: [PATCH 7/7] Update changelog --- src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj index 260225ff..7c223944 100644 --- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj +++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj @@ -24,6 +24,7 @@ Generates optimized IL code through the new resumable state machines, and comes Release notes: 0.2.3 + - improve TaskSeq.empty by not relying on resumable state, #89 - do not throw exception for unequal lengths in TaskSeq.zip, fixes #32 0.2.2 - removes TaskSeq.toSeqCachedAsync, which was incorrectly named. Use toSeq or toListAsync instead.