Repository navigation
feat: Queue and IQueue modules with the vocabulary of the FSharp.Core List module - #33
Open
xperiandri wants to merge 4 commits into
Open
xperiandri wants to merge 4 commits into
xperiandri wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
The broad breaking API rewrite needs human compatibility and cross-framework validation, with exception parity still unresolved.
1 open issue
What changed in this PR
Rewrites the queue bindings around FSharp.Core List vocabulary, continuing ADR 0001’s per-collection module design.
Changes:
- Adds concrete and interface queue APIs with List-compatible semantics.
- Adds Option/Tuple variants, shared enumeration helpers, and
queue { }. - Expands contract tests and documents breaking changes.
| File | Description |
|---|---|
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueTupleTests.fs | Tests reference-tuple operations. |
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueTests.fs | Tests queue behavior and exceptions. |
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueOptionTests.fs | Tests option-returning operations. |
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueOps.fs | Connects Queue to shared contracts. |
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueContractTests.fs | Instantiates queue contract suites. |
| tests/FSharp.Collections.Immutable.Tests/Queue/QueueComputationExpressionTests.fs | Tests computation expression behavior. |
| tests/FSharp.Collections.Immutable.Tests/Queue/IQueueTupleTests.fs | Tests interface tuple variants. |
| tests/FSharp.Collections.Immutable.Tests/Queue/IQueueTests.fs | Tests implementation preservation and enumeration. |
| tests/FSharp.Collections.Immutable.Tests/Queue/IQueueOptionTests.fs | Tests interface option variants. |
| tests/FSharp.Collections.Immutable.Tests/Queue/IQueueOps.fs | Connects IQueue to shared contracts. |
| tests/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj | Registers queue tests. |
| src/FSharp.Collections.Immutable/Queue/QueueTypes.fs | Defines queue type abbreviations. |
| src/FSharp.Collections.Immutable/Queue/QueueTuple.fsi | Declares tuple-variant API. |
| src/FSharp.Collections.Immutable/Queue/QueueTuple.fs | Implements tuple variants. |
| src/FSharp.Collections.Immutable/Queue/QueueOption.fsi | Declares option-variant API. |
| src/FSharp.Collections.Immutable/Queue/QueueOption.fs | Implements option variants. |
| src/FSharp.Collections.Immutable/Queue/QueueCE.fs | Implements the queue expression builder. |
| src/FSharp.Collections.Immutable/Queue/IQueueTuple.fsi | Declares interface tuple variants. |
| src/FSharp.Collections.Immutable/Queue/IQueueTuple.fs | Implements interface tuple variants. |
| src/FSharp.Collections.Immutable/Queue/IQueueOption.fsi | Declares interface option variants. |
| src/FSharp.Collections.Immutable/Queue/IQueueOption.fs | Implements interface option variants. |
| src/FSharp.Collections.Immutable/Queue/IQueue.fs | Implements interface queue operations. |
| src/FSharp.Collections.Immutable/Queue.fs | Removes the legacy module. |
| src/FSharp.Collections.Immutable/Helper.fs | Adds shared queue construction and traversal. |
| src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj | Registers the new queue files. |
| src/FSharp.Collections.Immutable/ComputationExpressions.fs | Exposes queue { }. |
| README.md | Updates supported collection table. |
| docsSrc/index.md | Lists interface and expression support. |
| docsSrc/Explanations/Background.md | Updates queue support documentation. |
| CHANGELOG.md | Records additions and breaking changes. |
Review effort: Balanced
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 18, 2026 18:49
56e9ddf to
e7a6257
Compare
Open
5 of 6 tasks
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 18, 2026 19:03
ec1f68a to
4bc0412
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 18, 2026 19:39
4bc0412 to
2245874
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 18, 2026 20:12
430f3fb to
53a3555
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 18, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 18, 2026 20:17
2245874 to
379bddd
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 18, 2026 23:05
53a3555 to
5c8b1f5
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 10:36
379bddd to
a7e7964
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 11:34
a7e7964 to
f92710a
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 19, 2026 14:49
67c2c1c to
4a66e46
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 14:49
f92710a to
b9b20d3
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 19, 2026 15:55
4a66e46 to
aa81bb5
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 15:55
b9b20d3 to
b539f98
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 19, 2026 17:31
aa81bb5 to
6ce859c
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 17:31
b539f98 to
a9f0d19
Compare
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 19, 2026 17:40
6ce859c to
d416a34
Compare
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 17:44
a9f0d19 to
ccef883
Compare
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
added a commit
that referenced
this pull request
Oct 19, 2026
…FSharp.Core `List` module Follows the review of #33 and ADR 0001 (D6, D7, D14): - `sort`, `sortByDescending`, `sortDescending` and `sortWith` of `Queue` and `IQueue` sort with the `Seq` functions of FSharp.Core, which share the stable sort of `List`, so that a comparison or a projection that throws throws what `List` throws. LINQ already wrapped the exception of a comparison in an `InvalidOperationException`, as `Array.Sort` does. `OrderByDescending` differed for the projection, which `List.sortByDescending` calls inside the comparison. `sortBy` and `sortWithComparer` keep LINQ. - `sortDescending` and `sortByDescending` call `Seq.sortWith` with the cached comparer of F# generic comparison, through the new `Queues.sortDescending` and `Queues.sortByDescending`. `Seq.sortDescending` and `Seq.sortByDescending` are inline: inside a generic function they compare with the generic operator, which boxes the elements of a value type in every comparison. For 10 000 integers, they allocate 7.8 MB per call instead of 0.74 MB. - At 10 000 integers, `sortWith` takes about the same time as before and allocates 743 KB per call instead of 440 KB. - `QueueCE.fsi` carries the signatures and the documentation of `QueueCE`, compiled right before it. `queue` has its `val` and its example in `ComputationExpressions.fsi`, after `indexedList`, and only the binding stays in the `.fs`. - `queue { yield! ... }` rejects a null sequence, array or queue with `ArgumentNullException` for the parameter `items`. - The remarks of `IQueue.append` say that the result keeps the implementation of the first queue, which receives the elements (D14), also when that queue is empty. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/queue
branch
from
October 19, 2026 18:10
ccef883 to
d53b2ab
Compare
…Core `List` module
`Queue` (`ImmutableQueue<'T>`) replaces the old module, which worked on `IImmutableQueue<'T>`. It has every function of
`List` that applies to an immutable queue, over the elements in the order in which they leave the queue, with the checks
and the exceptions of `List` and with stable sorts. Its parameters and exceptions name the queues `queue`, `queue1`,
`queue2`, `queue3` and `queues` where `List` names its lists. The extras of ADR 0001 are `enqueue`, `enqueueMany`,
`uncons`, `tryUncons`, the struct partial patterns `Cons` and `Nil`, `ofReadOnlySpan`, `ofSpan`, `skipLast`, `takeLast`,
`tryTail` and `sortWithComparer`; `enqueue`, `head`, `tail` and `uncons` compile as `Enqueue`, `Peek`, `Dequeue` and
`PeekDequeue`.
`IQueue` has the same functions for `IImmutableQueue<'T>` without the creation functions, plus `clear`. A result with the
element type of the input keeps the implementation of the input through `Clear` and `Enqueue` or through `Dequeue`; the
other results are immutable queues. The `Option` and `Tuple` namespaces repeat the functions that use value options or
struct tuples, and `queue { }` builds a queue.
A queue does not store its length, so `length` is O(n). `head`, `tail`, `skip`, `skipWhile`, `take`, `truncate` and
`splitAt` walk only the elements that they need, and the functions that check against the length take a snapshot first.
The `ImmutableQueue` module alias of the old module is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sequence-like, creation and stable sorting contracts run for `Queue`, and the sequence-like and stable sorting
contracts for `IQueue`, over immutable queues seen through the interface. `QueueOps.paramName` maps the names that the
`List` exceptions report to the names of the queue modules, also inside the names of several parameters and the
`list.[i]` names of `List.transpose`.
Further tests cover null inputs, the first-in, first-out order, `enqueue` and `enqueueMany`, `head`, `tail`, `uncons`
and `tryUncons`, the `Cons` and `Nil` patterns, queues whose elements lie in both stacks of `ImmutableQueue`, the results
that are their inputs, the order in which the functions of two queues call their callbacks before they throw, the
complete messages of the `List` exceptions with the names of the queues, the implementation that `IQueue` keeps,
`queue { }` and the `Option` and `Tuple` namespaces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The changelog lists the new functions of `Queue`, the new `IQueue` module, the `Option` and `Tuple` modules and
`queue { }`, the breaking changes of the `List` semantics, the queue type of the module and the patterns, the fixed
`forall` and `averageBy`, and the removed `clear` and `ImmutableQueue` alias. The collection tables name `queue { }` for
`Queue` and add `IQueue`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xperiandri
force-pushed
the
feat/indexed-list
branch
from
October 20, 2026 16:27
178bba1 to
53b8a6b
Compare
xperiandri
force-pushed
the
feat/queue
branch
from
October 20, 2026 16:27
d53b2ab to
931e95e
Compare
…ll the default modules by their short names Each of these files opens `FSharp.Collections.Immutable`, and inside `FSharp.Collections.Immutable.Tuple.IQueue` the name `IQueue` reaches the default module, also for the functions that both modules define, so the full namespace in front of it added nothing. The compiled code is the same: the opcodes and the called methods of every method of both builds match one to one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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
Rewrites the legacy
Queuemodule forImmutableQueue<'T>and adds the interface moduleIQueue(IImmutableQueue<'T>) after ADR 0001 (#23). This pull request is stacked on #31 (feat/indexed-list).Queue/Queue.fsiandQueue.fs: 124 functions, in the canonical category order. They are theQueuecolumn of ADR Appendix A, over the elements in the order in which they leave the queue. WhereListnames a list, the parameters name the queue:queue,queue1,queue2,queue3,queues.ImmutableQueuecome first (D5):Peek,Dequeue,Enqueue,IsEmpty.head,tail,uncons,skip,skipWhile,splitAt,takeandtruncateread only the elements that they need.Arrayfunction or a shared algorithm and creates the queue withImmutableQueue.Create. The elements go to the front stack, so the queue dequeues them without a reversal.List.sort*is.sort,sortDescending,sortWithandsortByDescendinguse theSeqsorts of FSharp.Core, which share the stable sort ofList, so a comparison or a projection that throws throws whatListthrows.sortByandsortWithCompareruse LINQ, which computes the keys first, asList.sortBydoes. A queue of one element is returned without a call of the projection or the comparer.lengthis O(n). A function that checks a count against the length reads the elements first.ofSeqreturns anImmutableQueuewithout a copy.enqueueMany,uncons,tryUncons,tryTail,skipLast,takeLast,sortWithComparer,ofReadOnlySpanandofSpan. The struct partial patternsConsandNilreplace the complete patternCons | Nilof 1.0.0.IQueue/IQueue.fsiandIQueue.fs: 115 functions overIImmutableQueue<'T>: the functions ofQueuewithout the creation functions, plusclear.Clear ()plusEnqueue, orDequeue.appendenqueues the second queue into the first one, so the collection that receives the elements keeps its implementation (D14), also when the first queue is empty.Queue.appendreturns the second queue then, because both areImmutableQueues.Queue. So areconcatandtranspose, because their inputs can have different implementations.Queue/QueueTypes.fsholds theQueue<'T>andIQueue<'T>abbreviations, because the modules are root modules (D2).Queue.fsiandQueue.fsopenFSharp.Collections.ImmutableafterSystem.Collections.Generic, whoseQueue<T>would shadow the abbreviation.Listfunction throws: the type, the message and the parameter name of this module. This includes the quirks ofList:exists2andforall2can return before they check the lengths.exceptdoes not read the items to exclude for an empty queue.take,skipandsplitAtthrow for a count beyond the length.InvalidOperationException, and so does the projection ofsortByDescending, whichListcalls inside the comparison. The projection ofsortBythrows as it is.nullinput throwsArgumentNullException.seqparameter rejectsnulland a default flat list given as a sequence, a boxed struct that the BCL would fail on while it enumerates it, withArgumentNullExceptionthat names the parameter.checkNotNullOrDefaultSeqdoes it as the first statement ofofSeq,concat,transpose,enqueueManyandexceptofQueueandIQueue(which has noofSeq) and ofyield!andforinqueue { }. The shared contracts of feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31 coverofSeq,concat,transposeandexcept; the tests here addenqueueMany, the covariant sequences (a default flat list of strings as objects) and the expression.Option.Queue(14 functions) andOption.IQueue(13) repeat the functions withvoption.Tuple.Queue(18) andTuple.IQueue(17) repeat the functions with struct tuples. The per-element tuple producers share one algorithm with the default module; the other functions delegate and repack.queue { }builds a queue. An immutable queue has no builder, so the expression collects its elements in aResizeArrayand creates the queue at the end. The first element of the expression leaves the queue first.yield!rejects anullsequence, array or queue and a default flat list given as a sequence withArgumentNullExceptionfor the parameteritems, andforrejects anullsequence and a default flat list for the parametersource; the checks run whenyield!orforis evaluated.QueueCE.fsicarries the signatures and the documentation, andqueuehas itsvalinComputationExpressions.fsi.Queuecompile under PascalCase names, where 1.0.0 compiled the F# names, such asheadandisEmpty. The queue operations use the .NET vocabulary in compiled code, in all six modules.head,tailandunconscompile asPeek,DequeueandPeekDequeue;tryHead,tryTailandtryUnconsasTryPeek,TryDequeueandTryPeekDequeue;enqueueandenqueueManyasEnqueueandEnqueueMany. The other functions use the compiled names of FSharp.Core.Helper/Queues.fs:Queuesholds the helpers that the queue modules share:ofFreshArray,ofBuffer,lastMatchand the walksfirstMatch,firstChoiceandfront. It is the only addition of this pull request to theHelperfolder.ImmutableQueuewith its struct enumerator, also when anIQueuefunction receives it, and any other queue with its own enumerator, which it disposes.IEnumerator<'T>type argument. An internal struct puts the struct enumerator of the queue behind that interface, and the loop calls it without boxing.IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31:Snapshot,ListChecks,ListArrays,Arrays,RaiseandErrorStrings, as the list modules do.sortDescendingandsortByDescendingof both modules callDescendingSortsof feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31, which avoids the boxes of the inlineSeqsorts.Lengths.queueUpToof feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31, only as far as a check needs. feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31 adds it without a caller, so the tests ofQueueandIQueueare the first to cover it.net10.0, bytes per call):Queue.tryFind,tryPickandfindIndex: 0 B.IQueue.tryFind,tryPickandfindIndexof anImmutableQueue: 0 B. The interface enumerator ofImmutableQueuewould allocate 48 B.Seq.sortDescendingandSeq.sortByDescending, and then withDescendingSortsof feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31, which the modules call (median of 200 calls, five alternating processes, one process for each sort):Queue.sortDescending: 7.6 MB and 2.2 ms, then 0.74 MB and 0.90 ms.Queue.sortByDescending: 7.6 MB and 2.3 ms, then 0.74 MB and 1.2 ms.IQueue.sortDescending: 7.8 MB and 1.9 ms, then 1.0 MB and 1.0 ms.IQueue.sortByDescending: 7.8 MB and 2.2 ms, then 1.0 MB and 1.2 ms.Queue.sortWith, is the same in both builds: 0.74 MB, 0.91 and 0.89 ms.Queue.fs.README.md,docsSrc/index.mdanddocsSrc/Explanations/Background.md:queue { }forQueueand a new row forIQueue.Tests: 9320 per target framework, 1488 of them new (768 for
Queue, 720 forIQueue; #31 has 7832).Queuederives the sequence-like (461 tests), creation (31) and stable-sort (15) contracts.IQueuederives the sequence-like (461) and stable-sort (15) contracts, over immutable queues upcast to the interface. The sequence-like and creation contracts of feat:IndexedListandIIndexedListmodules with the vocabulary of the FSharp.CoreListmodule #31 include the sequence inputs: a null sequence and a default flat list given as a sequence toofSeq,concat,transposeandexcept. The stable-sort contract also compares the exceptions of the sorts withList, checks the order in whichsortByDescendingcalls the projection, and checks that the descending sorts of 10 000 integers allocate less than 3 MB, which the inlineSeqfunctions do not meet.QueueOps.paramNamemaps the parameter names of theListexceptions to the names of the queue modules, also in"list1, list2"and in thelist.[i]names ofList.transpose.Queue(199) andIQueue(206) have one row for each case of a table: 114 and 115 functions that reject anullqueue, 12 and 10 results that are the input itself, 38 functions ofIQueuethat keep the implementation, and 9 and 9 functions that reject a default flat list given as a sequence (also a default flat list of strings as objects, which a type test misses, and an empty queue forexcept). The tests ofQueuealso have 6 sorts of one element, 4 functions of two queues that walk a common prefix and 14 complete messages. They also cover:ImmutableQueue;queue { }(20 tests, withyield!andforover a null sequence and a default flat list) and both namespaces (19, 17, 23 and 21 tests).IImmutableQueueimplementation proves thatIQueuekeeps it. A second one counts its enumerators: the searches,takeandtruncateread it only as far as they need and dispose the enumerator.Types of changes
What types of changes does your code introduce to FSharp.Collections.Immutable?
Put an
xin the boxes that applyThese changes break existing code:
Queueworks onQueue<'T>(ImmutableQueue<'T>) instead ofIQueue<'T>, andenqueue,tailandfilterreturn aQueue<'T>. The functions for anyIImmutableQueue<'T>are in the newIQueuemodule.tryFind,tryFindIndexandtryPickreturnvoption, andpickandtryPicktake functions that returnvoption.Cons | Nil, whoseConscase matched a reference tuple of the head and the tail, is replaced by the struct partial patternsCons, which matches a struct tuple, andNil.headandtailof an empty queue throwArgumentException, asList.headandList.taildo, where they threwInvalidOperationException.iter2of queues of different lengths throwsArgumentException, asList.iter2does, where it stopped at the shorter queue.forallandaverageBytake the queue. They returned a function of a sequence before.Queue.clearis removed: useQueue.empty, orIQueue.clearto keep the implementation of an interface value.Enqueue,PeekandDequeueand the other PascalCase compiled names, where it saw the F# names of 1.0.0, such asenqueue,headandtail.Checklist
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
Queues: they need an internal struct over the struct enumerator ofImmutableQueue. An inline loop with member constraints cannot use it: a constraint resolves only to members that are public everywhere, andIEnumerator<'T>has twoCurrentproperties. One loop over the interface enumerator would be simpler, at 48 B per call.exists,forall,takeWhile,skipand a few others look alike in both modules.Queuewalks the struct enumerator and builds anImmutableQueue;IQueuekeeps the implementation of its input.takeandtruncateread one element beyond the count. It tells them whether to return the input itself.List: the modules report the unusual names ofListexactly:"source"forexactlyOne,average,averageBy,containsand therandom*functions;"count"forinit.🤖 Generated with Claude Code