Skip to content

[Repo Assist] feat: add TaskSeq.scan and TaskSeq.scanAsync (ref #289)#296

Merged
dsyme merged 5 commits intomainfrom
repo-assist/feat-scan-2026-03-8079b757d4337d53
Mar 7, 2026
Merged

[Repo Assist] feat: add TaskSeq.scan and TaskSeq.scanAsync (ref #289)#296
dsyme merged 5 commits intomainfrom
repo-assist/feat-scan-2026-03-8079b757d4337d53

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 7, 2026

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

Summary

Adds TaskSeq.scan and TaskSeq.scanAsync — the "scan" analogue of fold/foldAsync, which returns the sequence of intermediate accumulator states rather than just the final value.

This is part of the ongoing work to align TaskSeq with FSharp.Control.AsyncSeq (ref #289).


Task 10 — Forward: TaskSeq.scan / TaskSeq.scanAsync

What is scan?

Like Seq.scan, but for async task sequences:

// fold returns only the final state
let! total = TaskSeq.ofList [1;2;3;4;5] |> TaskSeq.fold (+) 0
// total = 15

// scan returns all intermediate states (N+1 elements for N-element input)
let! running = TaskSeq.ofList [1;2;3;4;5] |> TaskSeq.scan (+) 0 |> TaskSeq.toListAsync
// running = [0; 1; 3; 6; 10; 15]

Changes

File Change
TaskSeqInternal.fs scan function using FolderAction DU (same pattern as fold)
TaskSeq.fsi scan and scanAsync signatures with XML doc
TaskSeq.fs Static member dispatch for scan/scanAsync
TaskSeq.Scan.Tests.fs 54 new tests covering empty, single-element, multi-element, string accumulation, immutable variants, and side-effect variants
FSharp.Control.TaskSeq.Test.fsproj Added TaskSeq.Scan.Tests.fs to compile order

Implementation

The implementation is a natural generalisation of fold: it yields the initial state, then for each element it updates the state and yields it again.

let scan folder initial (source: TaskSeq<_>) =
    checkNonNull (nameof source) source
    match folder with
    | FolderAction folder -> taskSeq {
        let mutable state = initial
        yield state
        for item in source do
            state <- folder state item
            yield state
      }
    | AsyncFolderAction folder -> taskSeq {
        let mutable state = initial
        yield state
        for item in source do
            let! newState = folder state item
            state <- newState
            yield state
      }

Test Status

✅ Build succeeded (Release)
✅ 3598 tests passed, 2 skipped (infrastructure skips, pre-existing)
✅ Fantomas formatting check passes
✅ 54 new scan/scanAsync tests all pass

Generated by Repo Assist ·

To install this agentic workflow, run

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

Implements scan/scanAsync (issue #289 — align with AsyncSeq).

scan: like fold but yields the initial state and each intermediate
accumulator state. Output has N+1 elements for N-element input.
scanAsync: async variant.

- TaskSeqInternal.fs: scan using FolderAction DU (matches fold pattern)
- TaskSeq.fsi: signatures with XML doc for scan/scanAsync
- TaskSeq.fs: static member dispatch (scan/scanAsync → Internal.scan)
- TaskSeq.Scan.Tests.fs: 54 tests (empty, single, multi, side-effects)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme
Copy link
Contributor

dsyme commented Mar 7, 2026

/repo-assist update release notes, and update AGENTS.md to make it clear the release notes need to be updated

…equirement

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 7, 2026

Commit pushed: ad62805

Generated by Repo Assist

@dsyme dsyme merged commit 43eff83 into main Mar 7, 2026
4 checks passed
@dsyme dsyme deleted the repo-assist/feat-scan-2026-03-8079b757d4337d53 branch March 7, 2026 22:15
github-actions bot pushed a commit that referenced this pull request Mar 7, 2026
…289)

- TaskSeq.chunkBySize: divides a task sequence into non-overlapping chunks of
  at most chunkSize elements. Uses a fixed-size array buffer (vs. ResizeArray)
  to avoid intermediate allocations and resizing.

- TaskSeq.windowed: returns overlapping sliding windows of a fixed size.
  Uses a ring buffer internally so that only a single allocation (per window)
  is needed; no redundant element copies on each step.

Both functions validate their size argument eagerly (before enumeration starts),
raise ArgumentException for non-positive sizes, and are fully documented in the
.fsi signature file.

Also:
- Update README.md to mark chunkBySize, windowed, pairwise, scan/scanAsync,
  reduce/reduceAsync, and unfold/unfoldAsync as implemented (these were merged
  in PRs #293, #296, #299, #300 respectively).
- Update release-notes.txt for 0.5.0.
- 171 new tests across TaskSeq.ChunkBySize.Tests.fs and TaskSeq.Windowed.Tests.fs.
  All 4021 existing tests continue to 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.

1 participant