Skip to content

[Repo Assist] Perf/code: replace ref cells with mutable in ~30 API functions#295

Draft
github-actions[bot] wants to merge 2 commits intomainfrom
repo-assist/modernize-ref-to-mutable-2026-04-01-410508f58ff02719
Draft

[Repo Assist] Perf/code: replace ref cells with mutable in ~30 API functions#295
github-actions[bot] wants to merge 2 commits intomainfrom
repo-assist/modernize-ref-to-mutable-2026-04-01-410508f58ff02719

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot commented Apr 1, 2026

🤖 This PR was created by Repo Assist, an automated AI assistant.

Modernises ~30 public API functions to use mutable local variables instead of heap-allocated ref cells (!/:= operators). This has two benefits:

Performance (Task 8): ref cells are heap-allocated objects — every let x = ref v allocates a new Ref<'T> on the heap. Replacing them with mutable locals eliminates these allocations in frequently-called hot paths (e.g. toArrayAsync, tryLast, zip*, skip*, take*, scan*), reducing GC pressure.

Code clarity (Task 5): The ref/!/:= style is a legacy pattern from early F#. Modern F# uses mutable/<- which is more readable and consistent with the rest of the codebase (many optimised enumerators already use let mutable).

Affected functions

tryLast, tryFirst, tryItem, compareWithAsync, reduceAsync, scanAsync, pairwise, windowed, pickAsync, tryPickAsync, tryFindIndex, tryFindIndexAsync, threadStateAsync, zipWithAsync, zipWithAsyncParallel, zipWithAsync3, allPairs, takeWhileAsync, takeUntilSignal, skipWhileAsync, skipWhileInclusiveAsync, skipUntilSignal, tryTail, splitAt, toArrayAsync, concatSeq, interleaveChoice, chunkBySize, chunkByAsync, mergeChoiceEnum, distinctUntilChangedWithAsync, emitEnumerator, removeAt, updateAt, insertAt, ofObservableBuffered, Disposables.Dispose

Note: infrastructure object-expression code (e.g. tryWith, tryFinally, collectSeq) that captures state across multiple method calls still uses ref — that is correct and intentional, as mutable cannot be captured across closure boundaries.

Changes

  • src/FSharp.Control.AsyncSeq/AsyncSeq.fs — ~30 functions modernised
  • RELEASE_NOTES.md — 4.11.0 entry updated

Test Status

✅ Build succeeded (0 errors, pre-existing FS9999 warning only for groupByAsync).

✅ All 372 tests pass.

Generated by 🌈 Repo Assist at {run-started}. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@1f672aef974f4246124860fc532f82fe8a93a57e

Modernises ~30 public API functions to use 'mutable' local variables
instead of heap-allocated 'ref' cells (! and := operators). This
reduces GC pressure in hot paths and aligns the code with idiomatic
modern F#.

Affected functions: tryLast, tryFirst, tryItem, compareWithAsync,
reduceAsync, scanAsync, pairwise, windowed, pickAsync, tryPickAsync,
tryFindIndex, tryFindIndexAsync, threadStateAsync, zipWithAsync,
zipWithAsyncParallel, zipWithAsync3, allPairs, takeWhileAsync,
takeUntilSignal, skipWhileAsync, skipWhileInclusiveAsync,
skipUntilSignal, tryTail, splitAt, toArrayAsync, concatSeq,
interleaveChoice, chunkBySize, chunkByAsync, mergeChoiceEnum,
distinctUntilChangedWithAsync, emitEnumerator, removeAt, updateAt,
insertAt, ofObservableBuffered, Disposables.Dispose.

All 372 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants