From c9f88449b79b3ef456a0c64f4264e130321563b8 Mon Sep 17 00:00:00 2001 From: Repo Assist Date: Sat, 7 Mar 2026 19:19:14 +0000 Subject: [PATCH 1/4] perf: remove unnecessary Lazy allocation in TaskSeq.init per iteration The `init` and `initInfinite` functions previously wrapped each yielded value in a `Lazy<'T>` object on every iteration, then immediately discarded it. This caused one heap allocation per element with no benefit: - `Lazy<_>.Create(fun () -> init i)` allocates a Lazy + closure - `value.Force()` goes through the Lazy lock-check machinery - `value <- Unchecked.defaultof<_>` immediately nulls it out The original rationale was thread safety for concurrent enumerator access, but IAsyncEnumerator explicitly does not support concurrent use, and the pattern did not actually protect against all concurrent access bugs. Replace with direct calls: `yield init i` and `let! result = asyncInit i; yield result`. Also includes prerequisite fix: bare range expressions `{ 1..10 }` in TaskSeq.Concat.Tests.fs replaced with list literals `[1..10]` to fix FS3873 deprecation error on newer F# compilers (>= 9.x / .NET SDK 10.x). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TaskSeq.Concat.Tests.fs | 4 ++-- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 22 +++---------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs index eefef383..ef8265f6 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs @@ -249,8 +249,8 @@ module SideEffect = let mutable i = 0 taskSeq { - yield ResizeArray { 1..10 } - yield ResizeArray { 1..10 } + yield ResizeArray [1..10] + yield ResizeArray [1..10] yield ResizeArray( diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index 66a92f2d..b9f94627 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -277,7 +277,6 @@ module internal TaskSeqInternal = let init count initializer = taskSeq { let mutable i = 0 - let mutable value: Lazy<'T> = Unchecked.defaultof<_> let count = match count with @@ -290,28 +289,13 @@ module internal TaskSeqInternal = match initializer with | InitAction init -> while i < count do - // using Lazy gives us locking and safe multiple access to the cached value, if - // multiple threads access the same item through the same enumerator (which is - // bad practice, but hey, who're we to judge). - if isNull value then - value <- Lazy<_>.Create(fun () -> init i) - - yield value.Force() - value <- Unchecked.defaultof<_> + yield init i i <- i + 1 | InitActionAsync asyncInit -> while i < count do - // using Lazy gives us locking and safe multiple access to the cached value, if - // multiple threads access the same item through the same enumerator (which is - // bad practice, but hey, who're we to judge). - if isNull value then - // TODO: is there a 'Lazy' we can use with Task? - let! value' = asyncInit i - value <- Lazy<_>.CreateFromValue value' - - yield value.Force() - value <- Unchecked.defaultof<_> + let! result = asyncInit i + yield result i <- i + 1 } From 54ff86731a661e2e32c00e12dac7999826cb465f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 7 Mar 2026 19:22:19 +0000 Subject: [PATCH 2/4] ci: trigger checks From fa88144a3f2e324627c287884ad5710063dfcdb3 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sat, 7 Mar 2026 19:32:23 +0000 Subject: [PATCH 3/4] format --- src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs index ef8265f6..5dd8e34b 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Concat.Tests.fs @@ -249,8 +249,8 @@ module SideEffect = let mutable i = 0 taskSeq { - yield ResizeArray [1..10] - yield ResizeArray [1..10] + yield ResizeArray [ 1..10 ] + yield ResizeArray [ 1..10 ] yield ResizeArray( From 6d2dd11c94701cbc5c7b0af820c0d02a9c6df94d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sat, 7 Mar 2026 19:46:08 +0000 Subject: [PATCH 4/4] update engineering --- global.json | 6 ++++++ release-notes.txt | 4 ++++ .../FSharp.Control.TaskSeq.SmokeTests.fsproj | 2 +- .../FSharp.Control.TaskSeq.Test.fsproj | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 00000000..badcd443 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.103", + "rollForward": "minor" + } +} diff --git a/release-notes.txt b/release-notes.txt index 7f0d630c..18ae26c7 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -1,5 +1,9 @@ Release notes: + +0.5.0 + - update engineering to .NET 9/10 + 0.4.0 - overhaul all doc comments, add exceptions, improve IDE quick-info experience, #136, #220, #234 - new surface area functions, fixes #208: diff --git a/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj b/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj index 83587f71..7b3f3e99 100644 --- a/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj +++ b/src/FSharp.Control.TaskSeq.SmokeTests/FSharp.Control.TaskSeq.SmokeTests.fsproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 True diff --git a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj index dbf1cfc5..dad6ebed 100644 --- a/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj +++ b/src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 True