From 5cfabe2c2283401da119106ce8da03ed96adb727 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:54:05 +0000 Subject: [PATCH] Improve: replace ref cell with mutable in mergeAll AsyncSeq.mergeAll used a ref for its internal live-task counter, which allocates a Ref wrapper object on the heap per call. This was the last remaining ref cell of this kind in the codebase after prior modernization passes (ofSeq, tryWith, tryFinally, pairwise, distinctUntilChangedWithAsync). Replaced with a direct mutable local for consistency and to avoid the allocation. No behavioral change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 1 + src/FSharp.Control.AsyncSeq/AsyncSeq.fs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d96928c..586091e 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,6 +3,7 @@ * Test coverage: Added tests for previously-untested public API functions `AsyncSeq.tryFirst`, `AsyncSeq.firstOrDefault`, `AsyncSeq.zipWithParallel`, `AsyncSeq.combineLatestWithAsync`, and `AsyncSeq.toObservable`. No functional changes. * Fixed Fable CI build: `Microsoft.Bcl.AsyncInterfaces` was pinned to a specific version (`10.0.7`) that was older than the version resolved transitively via `System.Threading.Channels`, causing a `NU1605` package downgrade error that made Fable's project cracker fail during `dotnet fable`. The reference now uses `Version="*"` (matching `System.Threading.Channels`) so both resolve consistently. (#334) * Tests: Added comprehensive tests for `AsyncSeq.zapp`, `AsyncSeq.zappAsync`, and `AsyncSeq.compareWithAsync`, which previously had no dedicated test coverage. +* Performance: Replaced the last remaining `ref` cell in `AsyncSeq.mergeAll`'s internal counter with a direct `mutable` local, matching the `ref`-to-`mutable` modernisation already applied elsewhere in the codebase. Eliminates a per-call heap allocation of a `Ref` wrapper; no behavioral change. ### 4.17.0 diff --git a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs index a6bb766..46cc7ff 100644 --- a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs +++ b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs @@ -2682,8 +2682,8 @@ module AsyncSeq = for i in 0 .. ss.Length - 1 do let! task = Async.StartChildAsTask (ies.[i].MoveNext()) do tasks.[i] <- task - let fin = ref n - while fin.Value > 0 do + let mutable fin = n + while fin > 0 do let! ti = Task.WhenAny (tasks) |> Async.AwaitTask let i = Array.IndexOf (tasks, ti) let v = ti.Result @@ -2697,7 +2697,7 @@ module AsyncSeq = | None -> let t = System.Threading.Tasks.TaskCompletionSource() tasks.[i] <- t.Task // result never gets set - fin.Value <- fin.Value - 1 + fin <- fin - 1 } let combineLatestWithAsync (f:'a -> 'b -> Async<'c>) (source1:AsyncSeq<'a>) (source2:AsyncSeq<'b>) : AsyncSeq<'c> =