Repository navigation
feat: add EquatableArray<'T> (step 1 of ADR 0002) - #28
Open
xperiandri wants to merge 4 commits into
Open
xperiandri wants to merge 4 commits into
xperiandri wants to merge 4 commits into
Conversation
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
The maintainers accepted every recommendation of section 11 on 2026-10-18, and pull request #28 implements step 1. - Status Accepted; section 11 records the decisions. - Section 1: the library compiles against the signature, `Slice` included. - Sections 5 and 6: step 1 also has `unsafeOfArray`, which throws for `null` like `ofArray`. - Section 7: the categories as implemented, the exact allocation counts, and the C# test project that the maintainers chose. - Section 9: the four differences the first consumer notices when it moves to the library type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 of 6 tasks
There was a problem hiding this comment.
🟡 Changes recommended
Recursive formatting can overflow the stack, and extreme reversed slice bounds can throw instead of returning an empty array.
2 open issues
What changed in this PR
Implements step 1 of ADR 0002: an immutable array wrapper with value semantics for F# and C# consumers.
Changes:
- Adds
EquatableArray<'T>, module functions, active patterns, JSON conversion, and collection interoperability. - Adds F# and C# tests for behavior and allocation guarantees.
- Updates project integration and documentation.
| File | Description |
|---|---|
| tests/FSharp.Collections.Immutable.Tests/TestCategories.fs | Centralizes F# test categories. |
| tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj | Includes tests and allocation settings. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayTestTypes.fs | Adds shared test models. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayPatternTests.fs | Tests active patterns. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayModuleTests.fs | Tests module functions. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayKeyTests.fs | Tests collection-key semantics. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayJsonTests.fs | Tests JSON integration. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayInteropTests.fs | Tests conversions, slices, and interfaces. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayFormattingTests.fs | Tests array formatting. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayEqualityTests.fs | Tests equality and hashing. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayDefaultValueTests.fs | Tests empty-default behavior. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayComparisonTests.fs | Tests ordering semantics. |
| tests/FSharp.Collections.Immutable.Tests/EquatableArray/EquatableArrayAllocationTests.fs | Checks Release allocations. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/TestCategories.cs | Centralizes C# test categories. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/MSTestSettings.cs | Configures parallel execution. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/JsonTests.cs | Tests record-struct serialization. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/IndexAndRangeTests.cs | Tests C# indices and ranges. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/FSharp.Collections.Immutable.CSharp.Tests.csproj | Adds the C# test project. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/EqualityTests.cs | Tests C# equality consumers. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/ConversionTests.cs | Tests conversions and compiled names. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/ComparisonTests.cs | Tests C# ordering consumers. |
| tests/FSharp.Collections.Immutable.CSharp.Tests/CollectionExpressionTests.cs | Tests collection expressions. |
| src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj | Includes implementations and signatures. |
| src/FSharp.Collections.Immutable/EquatableArray/EquatableArrayPatterns.fsi | Documents active-pattern signatures. |
| src/FSharp.Collections.Immutable/EquatableArray/EquatableArrayPatterns.fs | Implements active patterns. |
| src/FSharp.Collections.Immutable/EquatableArray/EquatableArray.fsi | Defines the documented public API. |
| src/FSharp.Collections.Immutable/EquatableArray/EquatableArray.fs | Implements the wrapper and supporting APIs. |
| README.md | Lists the new collection. |
| FSharp.Collections.Immutable.slnx | Includes C# tests. |
| FSharp.Collections.Immutable.slnf | Includes C# tests in the filter. |
| docsSrc/index.md | Lists the new feature. |
| docsSrc/Explanations/Background.md | Updates available collections. |
| Directory.Packages.props | Adds the JSON test dependency. |
| CHANGELOG.md | Records the new API. |
| .github/copilot-instructions.md | Updates project structure and library references. |
Review effort: Balanced
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
The maintainers accepted every recommendation of section 11 on 2026-10-18, and pull request #28 implements step 1. - Status Accepted; section 11 records the decisions. - Section 1: the library compiles against the signature, `Slice` included. - Sections 5 and 6: step 1 also has `unsafeOfArray`, which throws for `null` like `ofArray`. - Section 7: the categories as implemented, the exact allocation counts, and the C# test project that the maintainers chose. - Section 9: the four differences the first consumer notices when it moves to the library type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/equatable-array-type
branch
from
October 18, 2026 13:03
e657911 to
33b1a65
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
Section 2 states the depth guard of the display text: each nested display starts a new %A formatter, so a guard shared by them shows "..." at a nesting of 100 (the Copilot review of pull request #28). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A struct over one `ImmutableArray<'T>` field, as ADR 0002 designs it (step 1 of its section 6): - Equality and the hash code read all elements and compare them with `EqualityComparer<'T>.Default`, for every caller, C# `==` and `!=` included. - Comparison puts shorter arrays first, then orders by the first different element in the order of F# generic comparison; strings compare ordinally. - The default value is the empty array; `Items` never exposes a default `ImmutableArray`. - Conversions to and from `ImmutableArray<'T>` do not copy. `GetSlice` has the rules of F# array slices, `Slice` serves C# ranges, `IList<'T>` is read-only, a collection builder serves C# collection expressions, and a System.Text.Json converter factory needs no registration. - The module `EquatableArray` (compiled `EquatableArrayModule`): `empty`, `ofArray`, `ofImmutableArray`, `ofList`, `ofSeq`, `singleton`, `unsafeOfArray`, `isEmpty`, `length`, `map`, `toImmutableArray`. - The struct partial active patterns `Arr0` to `Arr3` and `ArrN`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One test class per behaviour: the module functions, the default value, equality under every caller, comparison, the key split of array elements (decision 5 of ADR 0002), formatting, the interop members, JSON under System.Text.Json and FSharp.SystemTextJson, the active patterns, and allocation-free equality, hashing, comparison, loops and matches in Release builds, where tiered compilation is off so that the counts are exact. `TestCategories.fs` holds the category attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A C# MSTest project covers what only the C# compiler binds: collection expressions through the collection builder, indices and ranges through `Slice`, the `==` and `!=` operators, the default collections, record structs, the implicit conversions and the module through its compiled names. The build and CI pick it up through the solution filter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds an Unreleased section to the changelog, the type to the collection lists, and its folders and libraries to the agent instructions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/equatable-array-type
branch
from
October 20, 2026 16:28
5d34d76 to
14747ab
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Proposed Changes
Implements step 1 of ADR 0002 (#24):
EquatableArray<'T>, a struct over oneImmutableArray<'T>with value semantics, for fields of records and unions, dictionary and set keys, and persisted documents.EqualityComparer<'T>.Default, for every caller: F# equality,Equals,EqualityComparer.Default, dictionary and set keys,ValueTuple, and C#==and!=.ImmutableArrayis never observable.ImmutableArray<'T>(constructor,Items,op_Implicit) do not copy.GetSlicehas the rules of F# array slices,Sliceserves C# ranges,IList<'T>is read-only,CollectionBuilderAttributeserves C# collection expressions, and a System.Text.Json converter factory needs no registration.EquatableArray(compiledEquatableArrayModule):empty,ofArray,ofImmutableArray,ofList,ofSeq,singleton,unsafeOfArray,isEmpty,length,map,toImmutableArray.Arr0,Arr1,Arr2,Arr3andArrN.The maintainers accepted the recommendations of ADR 0002 §11, and #24 records them. Step 2 (the mirror of the rewritten
FlatListmodule, the builder functions andequatableArray { }) follows in the chain of ADR 0001 (#23).Types of changes
What types of changes does your code introduce to FSharp.Collections.Immutable?
Put an
xin the boxes that applyChecklist
Put an
xin 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.Further comments
Tests. 97 F# tests in
tests/FSharp.Collections.Immutable.Tests/EquatableArray/cover one behaviour per class, with the category attributes in the newTestCategories.fs. 19 C# tests in the newtests/FSharp.Collections.Immutable.CSharp.Testsproject cover what only the C# compiler binds: collection expressions, indices and ranges,==and!=, record structs and the compiled names of the module. Release builds also check that=,hash,CompareTo,forand a pattern match allocate nothing. Tiered compilation is off in the F# test project for Release, so the counts are exact, and a control test proves that the counter sees an allocation. The full build chain as CI runs it passed locally (CI=true, Release, coverage on), and so did the strict documentation build.Differences from the wrapper of the first consumer (FSharp.Azure.Cosmos), which can replace it by opening this namespace:
ofArray nullandunsafeOfArray nullthrowArgumentNullException, as FSharp.Core does. The wrapper returned an empty array.NoComparison, so records and unions that hold the array gain comparison.EquatableArray<'T>. The patterns of the wrapper tookImmutableArray<'T>.ToString ()and%Aof an array wider than one line, or longer than 100 elements, wrap and end in..., as%Aof an F# array does. Short arrays print the same[|1; 2|].Implementation notes. The code is ported from the prototype that verified the ADR, with
Slice, the null checks,unsafeOfArrayand the XML documentation added. The signature files carry the documentation.ofSeqshares the array of a boxedImmutableArray(it cannot change) and throwsInvalidOperationExceptionfor a default one, asImmutableArray.CreateRangedoes.🤖 Generated with Claude Code