Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release-notes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Release notes:

0.6.0
- fixes: async { for item in taskSeq do ... } no longer wraps exceptions in AggregateException, #129
- performance: TaskSeq.replicate now uses a direct object-expression implementation, avoiding the taskSeq CE state machine and range IEnumerable/IEnumerator allocation
- adds TaskSeq.compareWith and TaskSeq.compareWithAsync
- adds TaskSeq.scan and TaskSeq.scanAsync, #289
- adds TaskSeq.pairwise, #289
Expand Down
19 changes: 16 additions & 3 deletions src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,22 @@ module internal TaskSeqInternal =
let replicate count value =
raiseCannotBeNegative (nameof count) count

taskSeq {
for _ in 1..count do
yield value
// Direct object-expression implementation: avoids the taskSeq CE state machine,
// the 1..count range IEnumerable, and its IEnumerator β€” all of which are unnecessary
// for this simple, always-synchronous sequence. MoveNextAsync always completes
// synchronously, which is the optimal fast path for ValueTask consumers.
{ new IAsyncEnumerable<'T> with
member _.GetAsyncEnumerator _ =
let mutable i = 0

{ new IAsyncEnumerator<'T> with
member _.MoveNextAsync() =
i <- i + 1
ValueTask<bool>(i <= count)

member _.Current = value
member _.DisposeAsync() = ValueTask.CompletedTask
}
}

/// Returns length unconditionally, or based on a predicate
Expand Down
Loading