Repository navigation
refactor: return voption and struct tuples from the collection functions - #47
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
The helpers preserve expected semantics, affected usages are updated, and the new behavior is comprehensively tested.
0 open findings
What changed in this PR
Introduces repository-wide collection helpers that return voption and struct tuples while preserving FSharp.Core behavior.
Changes:
- Adds shadowing
Seq,List, andArrayhelpers. - Updates affected call sites for new result types.
- Adds comprehensive emulator-free tests and project guidance.
| File | Description |
|---|---|
.github/copilot-instructions.md |
Documents the shared helpers and conventions. |
Directory.Build.props |
Includes helpers in applicable F# projects. |
src/Shared/ValueCollections.fs |
Implements collection shadows. |
src/Cosmos/Read.fs |
Handles the new voption result. |
tests/Cosmos.Tests/FSharp.Azure.Cosmos.Tests.fsproj |
Includes the new tests. |
tests/Cosmos.Tests/ReadExtensionsTests.fs |
Updates struct-tuple pattern matching. |
tests/Cosmos.Tests/TestCategories.fs |
Adds the test category attribute. |
tests/Cosmos.Tests/ValueCollectionsTests.fs |
Verifies behavior against FSharp.Core. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Should we stack it on top of Hedgehog and perform property testing? Or does not make sense? |
2872490 to
0aa9491
Compare
It makes sense, and it is done in #50. Stacking this pull request on the Hedgehog one is not possible, though, so the property tests come as a third step. Why it makes sense. Every function here hides the FSharp.Core function of the same name and must mean the same. That gives each one an oracle: another implementation that knows the expected answer for any input. A test can then run both on generated input and compare, with no expected values written by hand. That is the case property tests suit best. Why not by stacking. The dependency goes the other way: the adapter of #43 is written with these functions (its review asked for the repository's conventions in the ported code), so #43 is built on this branch. The order is #47, then #43, then #50. What #50 does. It replaces the three test methods of this pull request that compare the shadows with FSharp.Core on five hand-picked arrays by four methods marked Does it find more? I planted a defect that the hand-picked arrays do not reach: Nothing changes in this pull request: its hand-picked tests stay until #50 replaces them. |
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>
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>
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>
0aa9491 to
b62a75c
Compare
FSharp.Core's Seq, List and Array functions that look an element up return an option, and zip returns reference tuples: one heap object per result. src/Shared/ValueCollections.fs shadows them with functions of the same names and meaning that return a voption or struct tuples: tryHead, tryLast, tryItem, tryExactlyOne, tryFind, tryFindBack, tryFindIndex, tryFindIndexBack, tryPick, whose chooser returns a voption too, and zip. Seq.zip is Enumerable.Zip. Every F# project under src and tests compiles the file first, through the root Directory.Build.props, and gets an internal copy. The module opens itself in the namespace FSharp.Azure.Cosmos, so the shadows are in effect in the code of that namespace and in every file that opens it, while a function that only FSharp.Core has resolves to it as before. A file in another namespace opens FSharp.Azure.Cosmos to get them, and so does one in a child namespace such as FSharp.Azure.Cosmos.Tests: a child namespace does not open the modules of its parent. The build project is left out, because its scripts were generated against the functions of FSharp.Core. The two places that used such a function follow: the first item of a feed page in Read.fs and a zip in the ExistsAsync concurrency test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
b62a75c to
68f317f
Compare
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>
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>
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>
Proposed Changes
The repository prefers
voptionand struct tuples, but FSharp.Core's collection functions that look an element up return anoption, andzipreturns reference tuples. Every call then either allocates or needs a conversion at the call site. This was asked for in the review of #43 (helpers that returnvoption,zipoverEnumerable.Zip).src/Shared/ValueCollections.fsdefines functions with the names and the meaning of FSharp.Core's, forSeq,ListandArray:tryHead,tryLast,tryItem,tryExactlyOne,tryFind,tryFindBack,tryFindIndex,tryFindIndexBackreturn avoption;tryPickreturns avoptionand takes a chooser that returns one;zipreturns struct tuples.Seq.zipisEnumerable.Zip;Array.zipandList.zipstill reject collections of different lengths, as FSharp.Core's do.They shadow FSharp.Core's functions:
Seq.tryHeadnow means this one. A function that only FSharp.Core has, such asSeq.map, resolves to FSharp.Core as before, and a shadowed one stays reachable by its full name (Microsoft.FSharp.Collections.Seq.tryHead).How it reaches the code. The root
Directory.Build.propsadds the file as the firstCompileitem of every F# project undersrcandtests, so each project gets its own internal copy and a new project gets it without doing anything. The module isFSharp.Azure.Cosmos.ValueCollectionsand markedAutoOpen:FSharp.Azure.Cosmos, which includes modules declared asmodule FSharp.Azure.Cosmos.X, the shadows are in effect without anopen;open FSharp.Azure.Cosmos, which nearly every file of the library and of its tests has anyway;FSharp.Azure.Cosmos.Tests: a child namespace does not open the modules of its parent (tried out);The build project is left out: its scripts were generated against FSharp.Core's functions.
The two places on
mainthat used such a function follow: the first item of a feed page inRead.fsand azipin theExistsAsyncconcurrency test.Change after the review
The module was in the global namespace, so that the shadows were in effect in every file without an
open. As asked in the review, it is in the namespaceFSharp.Azure.Cosmosnow. The one file here that needed theopenfor it is the test of the shadows; the other two places that use them are inside the namespace or open it already. The branch is also rebased onto the currentmain.Types of changes
Internal refactoring; the public API of the package is unchanged.
Checklist
Further comments
ValueCollectionsTests(5 tests, categoryValueCollections, no emulator) runs every shadow and the FSharp.Core function it hides over the same inputs – empty, one element, repeated elements, a longer run; indexes before, inside and past the collection – and asserts equal answers.--strict.chooseandpick, whose choosers return an option as well, andMap.tryFind. They are not "try" functions of the three modules that the review named; say so if they should follow.🤖 Generated with Claude Code