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. diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index b94fbb26..8e4ae80c 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.False + member _.Current = Unchecked.defaultof<'T> + member _.DisposeAsync() = ValueTask.CompletedTask + } + } let isEmpty source = Internal.isEmpty source 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 }