Skip to content

[Repo Assist] perf: replace ValueOption tracking with while! in pairwise and distinctUntilChanged family - #411

Merged
dsyme merged 6 commits into
mainfrom
repo-assist/perf-pairwise-distinct-until-changed-20260427-ca209e6aaefc5cbe
May 1, 2026
Merged

[Repo Assist] perf: replace ValueOption tracking with while! in pairwise and distinctUntilChanged family#411
dsyme merged 6 commits into
mainfrom
repo-assist/perf-pairwise-distinct-until-changed-20260427-ca209e6aaefc5cbe

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

🤖 This is an automated pull request from Repo Assist, an AI assistant.

Summary

pairwise, distinctUntilChanged, distinctUntilChangedWith, and distinctUntilChangedWithAsync previously used a ValueOption mutable and a for current in source do loop to track the "previous" element:

// beforelet mutablemaybePrevious= ValueNone
for current in source domatch maybePrevious with| ValueNone -> maybePrevious <- ValueSome current
| ValueSome previous ->yield previous, current
maybePrevious <- ValueSome current

This PR replaces each with an explicit enumerator + while!, consistent with the pattern already used in except, exceptOfSeq, skipWhile, and similar functions:

// afteruse e = source.GetAsyncEnumerator CancellationToken.None
let!hasFirst= e.MoveNextAsync()if hasFirst thenlet mutableprevious= e.Current
while! e.MoveNextAsync()doletcurrent= e.Current
yield previous, current
previous <- current

Why this is better

  • Eliminates per-element struct match: Every element previously required a ValueNone / ValueSome branch. A direct mutable avoids this entirely.
  • Structural consistency: Uses the same while! + explicit enumerator idiom found throughout the rest of the optimised functions in TaskSeqInternal.fs.
  • Cleaner code: The first-element sentinel logic is made explicit via a single let! hasFirst = e.MoveNextAsync() check rather than inlined as a DU branch per element.

What changed

  • src/FSharp.Control.TaskSeq/TaskSeqInternal.fs: refactored pairwise, distinctUntilChanged, distinctUntilChangedWith, distinctUntilChangedWithAsync
  • release-notes.txt: entry under Unreleased

Test Status

  • dotnet build src/FSharp.Control.TaskSeq.sln -c Release — clean, 0 warnings, 0 errors
  • dotnet test (Release) — 5251 passed, 0 failed, 2 skipped
  • ✅ Targeted: Pairwise + DistinctUntilChanged suites — 94 passed, 0 failed
  • dotnet fantomas src/FSharp.Control.TaskSeq/TaskSeqInternal.fs --check — no formatting changes

Generated by 🤖 Repo Assist, see workflow run.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@96b9d4c39aa22359c0b38265927eadb31dcf4e2a

…ctUntilChanged family
pairwise, distinctUntilChanged, distinctUntilChangedWith, and
distinctUntilChangedWithAsync previously used a ValueOption mutable
and a for-in loop over the source to track the 'previous' element.
used in other optimized functions (except, exceptOfSeq, skipWhile, etc.).
This avoids the per-element struct match (ValueNone vs ValueSome branch)
and is structurally consistent with the rest of the library.
All 5251 tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme
dsyme marked this pull request as ready for review May 1, 2026 17:19
@dsyme
dsyme merged commit 0615138 into mainMay 1, 2026
5 checks passed
@dsyme
dsyme deleted the repo-assist/perf-pairwise-distinct-until-changed-20260427-ca209e6aaefc5cbe branch May 1, 2026 17:29
Sign up for freeto 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.

1 participant

@dsyme