From 82e30ea3e0336a13c06906095060c943ff08f21e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 01:29:17 +0000 Subject: [PATCH 1/2] test: add SideEffects module to TaskSeq.OfXXX.Tests.fs Documents re-iteration semantics for the ofXxx conversion functions: - ofSeq: re-evaluates the underlying IEnumerable on each re-enumeration - ofTaskSeq with lazy seq: creates fresh Task objects on each re-enumeration - ofTaskArray: tasks run once upfront; re-enumeration awaits cached results Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- release-notes.txt | 1 + .../TaskSeq.OfXXX.Tests.fs | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/release-notes.txt b/release-notes.txt index 72c765f9..b50493fd 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -2,6 +2,7 @@ Release notes: Unreleased + - test: add SideEffects module to TaskSeq.OfXXX.Tests.fs documenting re-iteration semantics (ofSeq re-evaluates source, ofTaskArray re-awaits cached tasks) - test: add SideEffects module and ImmTaskSeq variant tests to TaskSeq.ChunkBy.Tests.fs, improving coverage for chunkBy and chunkByAsync - fixes: `Async.bind` signature corrected from `(Async<'T> -> Async<'U>)` to `('T -> Async<'U>)` to match standard monadic bind semantics (same as `Task.bind`); the previous signature made the function effectively equivalent to direct application - refactor: simplify splitAt 'rest' taskSeq to use while!, removing redundant go2 mutable and manual MoveNextAsync pre-advance diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.OfXXX.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.OfXXX.Tests.fs index 11354a3d..cc493cbb 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.OfXXX.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.OfXXX.Tests.fs @@ -121,3 +121,76 @@ module Immutable = [] let ``TaskSeq-ofSeq should succeed`` () = Seq.init 10 id |> TaskSeq.ofSeq |> validateSequence + +module SideEffects = + [] + let ``ofSeq re-evaluates the underlying source seq on each re-enumeration`` () = task { + let mutable count = 0 + + // a lazy IEnumerable — each GetEnumerator() call re-executes the body + let lazySeq = seq { + for i in 1..3 do + count <- count + 1 + yield i + } + + let ts = TaskSeq.ofSeq lazySeq + let! arr1 = ts |> TaskSeq.toArrayAsync + // each item triggered the side effect once + count |> should equal 3 + + let! arr2 = ts |> TaskSeq.toArrayAsync + // the underlying seq is re-traversed on the second GetAsyncEnumerator call + count |> should equal 6 + arr1 |> should equal arr2 + } + + [] + let ``ofTaskSeq with lazy seq of tasks re-creates tasks on each re-enumeration`` () = task { + let mutable count = 0 + + // a lazy IEnumerable of Task objects — each seq iteration creates fresh Task objects + let lazyTaskSeq = seq { + for i in 1..3 do + yield task { + count <- count + 1 + return i + } + } + + let ts = TaskSeq.ofTaskSeq lazyTaskSeq + let! arr1 = ts |> TaskSeq.toArrayAsync + count |> should equal 3 + + let! arr2 = ts |> TaskSeq.toArrayAsync + // the underlying seq is re-iterated; new Task objects are created and run + count |> should equal 6 + arr1 |> should equal arr2 + } + + [] + let ``ofTaskArray does not re-run tasks on re-enumeration; task results are cached`` () = task { + let mutable count = 0 + + // tasks are created upfront; they run synchronously to completion when constructed + let tasks = + Array.init 3 (fun i -> task { + count <- count + 1 + return i + 1 + }) + + // all three tasks have already completed synchronously + count |> should equal 3 + + let ts = TaskSeq.ofTaskArray tasks + let! arr1 = ts |> TaskSeq.toArrayAsync + + // awaiting already-completed tasks does not re-run them + count |> should equal 3 + arr1 |> should equal [| 1; 2; 3 |] + + let! arr2 = ts |> TaskSeq.toArrayAsync + // the second enumeration re-awaits the same cached task results + count |> should equal 3 + arr2 |> should equal arr1 + } From b8e8e1187530ff81af527a4f04f52a9f458fc7cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Apr 2026 01:29:20 +0000 Subject: [PATCH 2/2] ci: trigger checks