Skip to content

feat: add FSharp.Azure.Cosmos.Sql with EquatableArray and the ImmutableArray patterns - #48

Open
xperiandri wants to merge 1 commit into
mainfrom
feat/cosmos-sql-equatable-array
Open

xperiandri wants to merge 1 commit into
mainfrom
feat/cosmos-sql-equatable-array

Conversation

@xperiandri

@xperiandri xperiandri commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Proposed Changes

This is the first part of #42, split off as asked there: the project FSharp.Azure.Cosmos.Sql with nothing but the two collection helpers that the syntax tree of the Cosmos DB SQL model (ADR 0001, #34) is built from, so that they can be reviewed on their own. #42 now builds on this branch and adds the syntax tree, the printer, the function catalog and the validator.

  • EquatableArray<'T> (src/Cosmos.Sql/EquatableArray.fs) is an immutable array whose equality compares the elements. ImmutableArray<'T> cannot hold the children of a syntax node: its own Equals compares the reference of the array it wraps. Two separately built nodes with equal children would then be equal only under the comparers that go through IStructuralEquatable, such as the equality F# generates for records and unions, and unequal inside a struct tuple, in a HashSet with the default comparer or for a C# caller. EquatableArray is a struct over an ImmutableArray with element-wise equality and a combined hash code; its default value behaves as an empty array and equals one. The EquatableArray module creates and maps the arrays: empty, ofImmutableArray, ofArray, unsafeOfArray, ofSeq, singleton, toImmutableArray, map. ofArray copies the array; unsafeOfArray wraps it without a copy, as ImmutableCollectionsMarshal.AsImmutableArray does, for a caller that gives the array away and never writes to it again. Two implicit conversions (op_Implicit), to and from ImmutableArray<'T>, wrap and unwrap without copying, so either type can be given where the other is expected. F# applies them without a warning only at method arguments; in a union case, a record field, an annotated binding or an argument of a let-bound function it applies them with warning FS3391, so F# code there keeps calling ofImmutableArray and toImmutableArray. C# applies them everywhere.
  • Arr0, Arr1, Arr2, Arr3, ArrN (src/Cosmos.Sql/ImmutableArrayPatterns.fs) are active patterns that match an ImmutableArray by its length, so that code over the children of a node reads like a list pattern without converting to an F# list. Each one returns a struct value option, or a bool, and its payload is a struct tuple, so a match allocates nothing.

The rest is the frame for these two files: the library project, the emulator-free MSTest project tests/Cosmos.Sql.Tests, the entries in the solution and the solution filter, and the solution tree of the agent instructions.

The library is not packed yet: IsPackable is false. A push to main publishes every packable project to GitHub Packages, and the two collection types alone are no package. #42 removes the setting in the commit that gives the package its metadata.

Types of changes

  • 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)

Checklist

  • 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

  • Tests: 15, category Collections, no emulator, in a file for the struct (EquatableArrayTests.fs), one for the module (EquatableArrayModuleTests.fs) and one for the patterns (ImmutableArrayPatternsTests.fs): equality and hashing, the default value, equality under the default comparer, in a struct tuple and in a HashSet, the read-only list view, the functions of the module, the implicit conversions (that they do not copy, and that a default array becomes an empty one in both directions), ToString, and the patterns for every length including a default array.
  • Moved to feat: add FSharp.Azure.Cosmos.Sql, a syntax tree, printer, validator and function catalog for Cosmos DB SQL #42: the two tests that compare whole syntax trees were part of EquatableArrayTests; they need the tree, so they are SyntaxTests there.
  • Review of feat: add FSharp.Azure.Cosmos.Sql, a syntax tree, printer, validator and function catalog for Cosmos DB SQL #42 applied here too: a documentation comment without tags is plain /// lines, and the test categories are sorted by name.
  • Verification: dotnet build FSharp.Azure.Cosmos.slnx in Debug and Release, 15 of 15 tests in both, Fantomas clean, the documentation builds with --strict, and dotnet pack produces no package for the project.
  • Review of 2026-10-11: the hash set is asserted with Assert.Contains, which asks the set itself and so still goes through the hash code; the tests are split into the three files above; unsafeOfArray is added, and the tests create their arrays with it, except the test that checks that ofArray and ofSeq copy.
  • Review (Copilot, 2026-10-11): the hash code test no longer asks unequal arrays to hash differently, which a hash code does not promise. It checks that every element is asked for its hash code once, first to last.
  • No changelog entry: nothing ships until feat: add FSharp.Azure.Cosmos.Sql, a syntax tree, printer, validator and function catalog for Cosmos DB SQL #42.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The focused implementation, documentation, project integration, and tests are consistent and complete.

0 open findings

What changed in this PR

Introduces the foundational collection helpers and project structure for the forthcoming Cosmos SQL model.

Changes:

  • Adds structurally equatable immutable arrays and allocation-free ImmutableArray active patterns.
  • Adds comprehensive emulator-free MSTest coverage.
  • Registers the new library and test projects in the solution.
File Description
.github/​copilot-instructions.md Documents the new project structure.
FSharp.Azure.Cosmos.slnf Adds projects to the solution filter.
FSharp.Azure.Cosmos.slnx Adds projects to the solution.
src/​Cosmos.Sql/​AssemblyInfo.fs Defines generated assembly metadata.
src/​Cosmos.Sql/​EquatableArray.fs Implements structural immutable arrays.
src/​Cosmos.Sql/​FSharp.Azure.Cosmos.Sql.fsproj Configures the non-packable library.
src/​Cosmos.Sql/​ImmutableArrayPatterns.fs Adds length-based active patterns.
tests/​Cosmos.Sql.Tests/​EquatableArrayTests.fs Tests collections and patterns.
tests/​Cosmos.Sql.Tests/​FSharp.Azure.Cosmos.Sql.Tests.fsproj Configures the unit-test project.
tests/​Cosmos.Sql.Tests/​TestCategories.fs Defines the collections test category.
tests/​Cosmos.Sql.Tests/​testconfig.json Enables parallel test execution.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

xperiandri added a commit that referenced this pull request Oct 10, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; Odd and Even stay inside their range; DateOnly
and TimeOnly get generators, upstream through
hedgehogqa/fsharp-hedgehog#488. The open question of the adapter's code
style is answered, with formatting and nullness checking still open.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, Pascal-case
assertion functions and ImmutableArray for shared test data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri added a commit that referenced this pull request Oct 10, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The open question of the adapter's code
style is answered, with formatting and nullness checking still open.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, Pascal-case
assertion functions and ImmutableArray for shared test data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the feat/cosmos-sql-equatable-array branch from 8a4d5ff to a16aa6a Compare October 10, 2026 16:23
xperiandri added a commit that referenced this pull request Oct 10, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The open question of the adapter's code
style is answered, with formatting and nullness checking still open.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, Pascal-case
assertion functions and ImmutableArray for shared test data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri added a commit that referenced this pull request Oct 10, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The
open question of the adapter's code style is answered, formatting and
nullness checking included: the two projects are formatted with Fantomas
and build with nullness checking on, and only the verbatim port keeps
upstream's settings.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, in #51
with an instruction scoped to those files, Pascal-case assertion
functions and ImmutableArray for shared test data.

Two more answers of the same day: the Windows emulator lane of #45 runs
weekly instead of nightly (section 2), and the shared collection
functions of #47 are in the namespace FSharp.Azure.Cosmos instead of the
global one (section 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri added a commit that referenced this pull request Oct 10, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The
open question of the adapter's code style is answered, formatting and
nullness checking included: the two projects are formatted with Fantomas
and build with nullness checking on, and only the verbatim port keeps
upstream's settings.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, in #51
with an instruction scoped to those files, Pascal-case assertion
functions and ImmutableArray for shared test data.

Two more answers of the same day: the Windows emulator lane of #45 runs
weekly instead of nightly (section 2), and the shared collection
functions of #47 are in the namespace FSharp.Azure.Cosmos instead of the
global one (section 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the feat/cosmos-sql-equatable-array branch 3 times, most recently from fa1d6fb to fe8b9ef Compare October 11, 2026 09:03
xperiandri added a commit that referenced this pull request Oct 11, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The
open question of the adapter's code style is answered, formatting and
nullness checking included: the two projects are formatted with Fantomas
and build with nullness checking on, and only the verbatim port keeps
upstream's settings.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, in #51
with an instruction scoped to those files, Pascal-case assertion
functions and ImmutableArray for shared test data.

Two more answers of the same day: the Windows emulator lane of #45 runs
weekly instead of nightly (section 2), and the shared collection
functions of #47 are in the namespace FSharp.Azure.Cosmos instead of the
global one (section 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread tests/Cosmos.Sql.Tests/EquatableArrayTests.fs Outdated
Comment thread tests/Cosmos.Sql.Tests/EquatableArrayTests.fs
Comment thread src/Cosmos.Sql/EquatableArray.fs
@xperiandri
xperiandri force-pushed the feat/cosmos-sql-equatable-array branch from fe8b9ef to 1031d6d Compare October 11, 2026 09:55
@xperiandri
xperiandri force-pushed the feat/cosmos-sql-equatable-array branch from 1031d6d to 7f0a02e Compare October 11, 2026 11:03
xperiandri added a commit that referenced this pull request Oct 11, 2026
The maintainer's reviews of 2026-10-10 changed several things the record
describes, and it now says so where each belongs.

The Hedgehog MSTest adapter (#43): the in-repository copy follows the
coding guidelines of this repository, while the upstream pull request
keeps upstream's style in a copy of its own, so a change of behaviour
goes into both; an invocation that MSTest could not run stops the run
instead of being shrunk; the cancellation token is checked after every
invocation, whatever its outcome, which the automatic review of #50
asked for; Odd and Even stay inside their range; DateOnly and TimeOnly
get generators, upstream through hedgehogqa/fsharp-hedgehog#488. The
open question of the adapter's code style is answered, formatting and
nullness checking included: the two projects are formatted with Fantomas
and build with nullness checking on, and only the verbatim port keeps
upstream's settings.

Collections (section 4): the shared collection functions of #47 that
return voption and struct tuples, tested with properties in #50, and the
rule of one module per pipeline of #49.

Build (section 5): why FAKE's DotNet.test cannot run the test
applications, and fsprojects/FAKE#2903, which adds DotNet.testMTP.

The spec model (sections 6 and 16): EquatableArray and the ImmutableArray
patterns are #48 and the model itself #42; the validator returns an
ImmutableArray of struct errors.

Golden baselines (section 13): a literal type provider was evaluated on
the question asked in #46 and rejected, with the reasons.

Tests (section 22): test category attributes sorted by name, in #51
with an instruction scoped to those files, Pascal-case assertion
functions and ImmutableArray for shared test data.

Two more answers of the same day: the Windows emulator lane of #45 runs
weekly instead of nightly (section 2), and the shared collection
functions of #47 are in the namespace FSharp.Azure.Cosmos instead of the
global one (section 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri added a commit that referenced this pull request Oct 11, 2026
Section 4 said that an F# record or union holding an ImmutableArray
loses structural equality. It does not: the equality the compiler
generates goes through IStructuralEquatable, which the array implements,
and compares the elements. What is true is narrower. The array's own
Equals, which EqualityComparer.Default calls, compares references, so a
struct tuple, a HashSet with the default comparer and a C# caller see
two equal arrays as different. The structural path boxes every element,
hashes only the last eight, throws on compare for different lengths,
and a default array never equals an empty one.

The section now gives these as the reasons for EquatableArray, as the
documentation comment of the type in #48 already does.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The hash-code tests incorrectly require unequal values to produce distinct randomized hash codes, making them potentially nondeterministic.

1 open finding

🧠 Review effort: Balanced

Comment thread tests/Cosmos.Sql.Tests/EquatableArrayTests.fs Outdated
…mutableArray` patterns

Starts src/Cosmos.Sql (FSharp.Azure.Cosmos.Sql, net10.0, FSharp.Core
only), the project of the Cosmos DB SQL model of ADR 0001 (sections 4, 6
and 15), with the two collection helpers that the syntax tree is built
from, so that they can be reviewed on their own before the tree, the
printer and the validator follow.

EquatableArray<'T> is a struct over ImmutableArray<'T> with sequence
equality and a combined hash code. ImmutableArray's own Equals and
EqualityComparer<T>.Default compare the reference of the wrapped array,
which a struct tuple, a default-comparer HashSet or a C# caller would
see, and a default array never equals an empty one. The nodes of the
syntax tree will hold their children in this wrapper, so that their
equality is structural under every comparer. The EquatableArray module
creates and maps the arrays; its unsafeOfArray wraps an array without
copying it, as ImmutableCollectionsMarshal.AsImmutableArray does, for a
caller that gives the array away. Implicit conversions to and from
ImmutableArray wrap and unwrap without copying; F# applies them without
a warning at method arguments, and with warning FS3391 elsewhere.

The struct partial active patterns Arr0, Arr1, Arr2, Arr3 and ArrN match
an ImmutableArray by its length without allocating: each returns a
struct value option or a bool, and its payload is a struct tuple.

tests/Cosmos.Sql.Tests is the new emulator-free MSTest project, with 15
tests under the category Collections: a file for the struct, one for the
module and one for the patterns. Both projects join the solution and the
solution filter, and the agent instructions list them. The library is
not packed yet (IsPackable is false): a push to main publishes every
packable project to GitHub Packages, and the collection types alone are
no package. The commit that gives the package its metadata removes the
setting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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