Skip to content

chore: lay the foundation for the per-collection modules - #25

Merged
xperiandri merged 10 commits into
mainfrom
chore/foundation
Oct 18, 2026
Merged

xperiandri merged 10 commits into
mainfrom
chore/foundation

Conversation

@xperiandri

@xperiandri xperiandri commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Proposed Changes

First link of the chain that implements ADR 0001 – Per-collection modules (#23): the foundation that the collection pull requests build on. It changes no public function yet.

  • Helper.fs replaces ImmutableCollectionUtil.fs (same internal module, so the legacy files compile unchanged) and adds checkNotDefault, which treats a default ImmutableArray as FSharp.Core treats a null array.
  • build/check-order.fsx checks every signature file: the functions are grouped under // ---- Category ---- banners in the canonical order of ADR 0001 D4, sorted ordinally inside a category (active patterns last), and the implementation file repeats the banners and the order. It reads val internal and val private declarations correctly. The FAKE target CheckOrder runs it before DotnetBuild; the README lists the target.
  • The library and the tests target net8.0 and net10.0 (D10); CI and the dev container already install both runtimes.
  • FSharp.Core 10.1.301 is the minimum: it is the first release with List.partitionWith, Array.partitionWith and Set.partitionWith, which the modules mirror.
  • The seven placeholder test files are replaced by:
    • TestCategories.fs with one category attribute per collection and per specialized area, sorted by type name;
    • TestCategoriesTests.fs, which enforces the category rules of ADR 0001 D8 and keeps the test run non-empty: one category per attribute, named after the type, the attributes sorted, a category attribute of this project on every test class, no category on a test method and no category written as a string;
    • shared helpers of the contract tests, as root modules: Contracts/Inputs.fs, Contracts/ExceptionAssert.fs (compares an exception with the one of the mirrored FSharp.Core function: type, first line of the message and parameter name, under the invariant culture, because FSharp.Core ships localized messages), Contracts/OverrideAssert.fs, and Contracts/Mirror.fs with the FSharp.Core module that a collection mirrors.
  • .github/copilot-instructions.md describes the new layout: the types files, the comparer structs, the extension modules, root modules and categories on the test classes.
  • The README and the documentation pages list the collections in a table that each following pull request updates.

Types of changes

What types of changes does your code introduce to FSharp.Collections.Immutable?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

The package requires FSharp.Core 10.1.301 or later.

Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

  • Build and tests pass locally
  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added necessary documentation (if appropriate)

Further comments

One deviation from ADR 0001 D9: the contract bases arrive with the first collection that implements them (FlatList brings the sequence-like facets, HashMap the map facets, HashSet the set facets) instead of this pull request, so each base is reviewed together with an implementation that runs it.

The collection pull requests are stacked on this one. Each one merges into the branch it is based on until this one is in main.

The test helpers move to arrays and data rows (the new Testing rules on main) in a follow-up push.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 18, 2026 10:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new ordering and operation-category checks can silently accept violations they are intended to reject.

3 open issues
What changed in this PR

Establishes the foundation for ADR 0001’s per-collection rewrite without changing public functions.

Changes:

  • Adds shared argument checks, contract-test helpers, and convention enforcement.
  • Targets .NET 8 and .NET 10 and raises the minimum FSharp.Core version.
  • Updates collection documentation and build guidance.
File Description
tests/​FSharp.Collections.Immutable.Tests/​TestCategoriesTests.fs Adds category convention checks.
tests/​FSharp.Collections.Immutable.Tests/​TestCategories.fs Defines centralized category attributes.
tests/​FSharp.Collections.Immutable.Tests/​Stack.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​Sets.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​Queue.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​Maps.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​IndexedSeq.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​ImmutableList.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​FSharp.Collections.Immutable.Tests.fsproj Registers helpers and both target frameworks.
tests/​FSharp.Collections.Immutable.Tests/​FlatList.fs Removes placeholder tests.
tests/​FSharp.Collections.Immutable.Tests/​Contracts/​OverrideAssert.fs Adds representation comparison helpers.
tests/​FSharp.Collections.Immutable.Tests/​Contracts/​Inputs.fs Adds shared contract inputs.
tests/​FSharp.Collections.Immutable.Tests/​Contracts/​ExceptionAssert.fs Adds exception comparison helpers.
src/​FSharp.Collections.Immutable/​ImmutableCollectionUtil.fs Removes the former helper file.
src/​FSharp.Collections.Immutable/​Helper.fs Preserves helpers and adds default-array validation.
src/​FSharp.Collections.Immutable/​FSharp.Collections.Immutable.fsproj Registers Helper.fs and both target frameworks.
README.md Adds collection table and CheckOrder guidance.
docsSrc/​index.md Adds collection table.
docsSrc/​Explanations/​Background.md Adds collection table.
Directory.Packages.props Raises FSharp.Core minimum version.
CHANGELOG.md Records framework and dependency changes.
build/​check-order.fsx Adds signature and implementation ordering checks.
build/​build.fs Runs CheckOrder before builds.
.github/​copilot-instructions.md Updates architecture and convention guidance.

Review effort: Balanced

Comment thread build/check-order.fsx Outdated
Comment thread build/check-order.fsx Outdated
Comment thread tests/FSharp.Collections.Immutable.Tests/TestCategoriesTests.fs
xperiandri added a commit that referenced this pull request Oct 18, 2026
`build/check-order.fsx` (review of #25):

- understands file-scoped modules (`module A.B` without `=`), whose
  members start at column 0, and `module rec`;
- reports a `val` at column 0 outside every module that it understands,
  instead of passing an unknown header unchecked;
- compares the category of each function of the implementation with the
  signature, so that a moved banner is reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri requested a balanced review from Copilot October 18, 2026 15:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The mandatory order checker rejects valid implementation helpers whose visibility is restricted by the signature.

1 open issue
3 resolved since last review

Review effort: Balanced

Comment thread build/check-order.fsx Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread build/check-order.fsx Outdated
Comment thread docsSrc/Explanations/Background.md
Comment thread tests/FSharp.Collections.Immutable.Tests/Contracts/ExceptionAssert.fs Outdated
Comment thread tests/FSharp.Collections.Immutable.Tests/Contracts/OverrideAssert.fs Outdated
Comment thread README.md
xperiandri and others added 7 commits October 18, 2026 19:33
Prepares the chain of ADR 0001, one pull request per collection:

- `Helper.fs` replaces `ImmutableCollectionUtil.fs` and adds
  `checkNotDefault`, which treats a default `ImmutableArray` as
  FSharp.Core treats a `null` array.
- `build/check-order.fsx` checks that every signature file groups its
  functions under category banners in the canonical order, sorts them
  alphabetically inside a category and that the implementation file
  repeats that order. The FAKE target `CheckOrder` runs it before
  `DotnetBuild`.
- The library and the tests target `net8.0` and `net10.0`; FSharp.Core
  10.1.301 is the first release with `partitionWith`.
- The placeholder tests are replaced by the category attributes of
  every collection, tests that enforce the category rules, and the
  shared helpers of the contract tests: inputs, the comparison of an
  exception with the FSharp.Core oracle, and the comparison of the
  `Option` and `Tuple` overrides with the default functions.
- The README and the documentation pages list the collections in a
  table that each following pull request updates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`build/check-order.fsx` (review of #25):

- understands file-scoped modules (`module A.B` without `=`), whose
  members start at column 0, and `module rec`;
- reports a `val` at column 0 outside every module that it understands,
  instead of passing an unknown header unchecked;
- compares the category of each function of the implementation with the
  signature, so that a moved banner is reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows the reworked ADR 0001:

- Test categories go on the test classes only (D8). `TestCategoriesTests` checks that
  no test class has a category written as a string and that test methods carry no
  categories, instead of an operation category on every method; its own methods lose
  their categories. `TestCategories.fs` states the rule.
- Root modules (D2): `ExceptionAssert`, `Inputs` and `OverrideAssert` are the root
  modules of their files. The `Mirror` union moves to `Contracts/Mirror.fs`, so it stays
  in the namespace.
- `check-order.fsx` reads the access modifier of `val internal`/`val private`, so an
  internal value no longer counts as a public function named `internal`.
- The copilot instructions list the types files, the comparer structs, the extension
  modules and the rules of the reworked ADR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tests compare arrays and take their cases as data rows:

- `Inputs` is a static class of arrays, so that a test can name its properties in
  `DynamicData (nameof Inputs.All, typeof<Inputs>)`; `typeof` cannot name a module.
  `All` and `NonEmpty` are the row sources of the tests that run on every input. Every
  property builds new arrays on each read, so that no test sees the rows of another.
- `OverrideAssert.pairMatches` and `tripleMatches` compare with F# structural equality:
  the equality of a tuple compares arrays by reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `<summary>` of `ExceptionAssert.headline` spans several lines, as its `<remarks>` does: a summary and a remarks
stay on one line each only when both are short.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The solution structure lists the files that every folder has:

- Every `XOption` and `XTuple` file has a line of its own, as the `FlatList` files do.
- The comparer struct and the extension files take their dotted names
  (`HashMap.CustomComparers`, `HashMap.Extensions`, `HashSet.CustomComparer`, ...) and nest
  under the line of their module; they carry `<DependentUpon>`, so that Visual Studio shows
  them as a tree.
- The builder and computation expression files and `ComputationExpressions.fs` have their
  signature files, as the rule above the tree asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The order check parses the files with Fantomas.FCS instead of regular
expressions and runs inside the build project as `CheckOrder.fs`; the
signature decides which implementation bindings take part in the order.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread build/CheckOrder.fs
The order check runs the signature files in parallel: each pair is
independent, so `Array.Parallel.collect` keeps the violations in file
order without a shared list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new mandatory parser-based build gate warrants human validation against representative signatures across both target frameworks.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

The contract assert helpers are static classes with methods and an
optional `message`, like the `Assert` class of MSTest:
`ExceptionAssert.Same` / `Mirrored` / `Headline` and
`OverrideAssert.OptionMatches` / `PairMatches` / `TripleMatches`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment thread tests/FSharp.Collections.Immutable.Tests/Contracts/ExceptionAssert.fs Outdated
The optional `message` of the contract assert helpers is a struct
optional parameter, so it is a `voption`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri merged commit a27cfcf into main Oct 18, 2026
7 checks passed
@xperiandri
xperiandri deleted the chore/foundation branch October 18, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants