Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`tryGetValue` with options, and `partition` and `partitionWith` with reference tuples.
* `hashSet { }` builds a hash set, and `HashSetCE` created with an equality comparer builds hash sets that compare with it;
a `null` comparer throws `ArgumentNullException`.
* `SortedSet` has every function of the FSharp.Core `Set` module, with its semantics, argument order and exceptions, plus
`addRange`, `build`, `builder`, `choose`, `clear`, `comparer`, `forComparer`, `indexOf`, `item`, `ofBuilder`,
`ofReadOnlySpan`, `ofSpan`, `overlaps`, `removeRange`, `rev`, `setEquals`, `symmetricDifference`, `toBuilder`,
`tryGetValue`, `tryItem` and `update`.
* `SortedSet.addRange elements set` and `removeRange elements set` take the place of the `Union` and `Except` members that
take a sequence. The result keeps the comparer of the sorted set, and the input itself comes back when nothing changes.
`removeRange` wraps `Except`. `addRange` adds the elements one by one, so an element of the sequence that the comparer
finds equal to an element of the sorted set never replaces it, and of several equal elements of the sequence the first
is added. `Union` does not promise that: when the sequence is large against the sorted set, it refills the tree from an
unstable sort and can keep an element of the sequence instead.
* `SortedSet.ofReadOnlySpan` and `ofSpan` create a sorted set from a span and keep the first of the elements that the
comparer finds equal, where `ImmutableSortedSet.Create` with a span keeps an arbitrary one of them. `build` fills a
builder with an action, and `update` applies an action to a builder of a sorted set and returns its contents, so that
the in-place operations of the builder, such as `IntersectWith`, are one expression.
* `SortedSet.forComparer comparer` creates the `CustomComparerSortedSet` struct, whose members `build`, `builder`, `empty`,
`ofArray`, `ofList`, `ofReadOnlySpan`, `ofSeq`, `ofSpan` and `singleton` create sorted sets and builders that order
their elements with the comparer, as in `(SortedSet.forComparer StringComparer.OrdinalIgnoreCase).ofSeq names`. They
replace the BCL members `Create(comparer, ...)`, `CreateRange(comparer, items)`, `ToImmutableSortedSet(items, comparer)`
and `CreateBuilder(comparer)`. `ofSeq` also gives an existing sorted set the comparer, as `WithComparer` does: the
sorted set itself comes back for the same comparer instance, and an equal but distinct instance rebuilds it, keeping the
first of the elements that the comparer finds equal. A `null` comparer throws `ArgumentNullException`.
The element type needs no F# comparison. The default value of the struct orders with `Comparer<'T>.Default`, as the BCL
does for a `null` comparer, so `WithComparer(null)` is `ofSeq` of the default value, where `SortedSet.ofSeq set` orders
with F# generic comparison.
* `SortedSet.add` and `remove` return the input itself when nothing changes, so a reference comparison of the input and
the result tells whether `Add` changed anything, as the `bool` result of `Builder.Add` does. Sorted sets have no builder
module: `builder`, `toBuilder` and `ofBuilder` are `CreateBuilder`, `ToBuilder` and `ToImmutable`, and the builder is
used directly. The mutating members of the BCL interfaces, such as `ISet<T>.UnionWith` and `ICollection<T>.Add`, throw
`NotSupportedException` on a sorted set and have no function.
* `add`, `addRange`, `remove`, `removeRange`, `clear`, `update`, `filter` and `partition` of `SortedSet` keep the comparer
of their input, and the set algebra keeps the comparer of the first sorted set. Every function throws
`ArgumentNullException` for a `null` sorted set, sequence or array, and for a default flat list that is given as a
sequence; `yield!` and `for` of `sortedSet { }` do the same. `unionMany` of an empty sequence returns an empty sorted
set, as `Set.unionMany` does. `tryGetValue` returns a `voption`, `choose` takes a function that returns a `voption`,
and `partition` and `partitionWith` return struct tuples.
* The functions of `SortedSet` that create a sorted set without a comparer, `builder`, `build`, `unionMany` of no sets,
`map`, `choose`, `partitionWith` and `sortedSet { }` order with F# generic comparison, as `Set` does, where the BCL
orders with `Comparer<'T>.Default`. A sorted set thus enumerates its elements in the order of the F# set of the same
elements: strings, and tuples and other values that contain strings, compare ordinally instead of in the order of the
current culture. `SortedSet.forComparer Comparer<'T>.Default` orders as the BCL does.
* The set algebra and the relations of `SortedSet` take sorted sets, as the `Set` functions do. The BCL members that take
a sequence map as follows: `Union` and `Except` are `addRange` and `removeRange`, `Overlaps` and `IsSupersetOf` are
`Seq.exists` and `Seq.forall` with `contains`, and the others take `SortedSet.ofSeq elements`, or
`(SortedSet.forComparer (SortedSet.comparer set)).ofSeq elements` to give the elements the comparer of the first sorted
set.
* The element that stays when the comparer finds two elements equal follows the BCL: `union` keeps the element of the
first sorted set, or of the larger one when both have the same comparer, `intersect` keeps the elements of the second
one, and the creation functions keep the first element of their input, where `CreateRange` and `Create` with a span keep
any of them. When the second sorted set of `union` has another comparer and is much larger, the BCL refills the tree
from an unstable sort and either element can stay; only `addRange` keeps the element of the sorted set in every case.
`isProperSuperset` is the exception: it counts the elements of the second sorted set that the comparer of the first one
finds equal once, as the documentation of `IsProperSupersetOf` says, where the member counts each of them.
* `SortedSet.minElement` and `maxElement` take the place of the properties `Min` and `Max`: they read the first and the
last element in O(log n) and throw `ArgumentException` for an empty sorted set, as `Set.minElement` and
`Set.maxElement` do, where the properties return `default`. `tryItem 0` and `tryItem (count - 1)` return a `voption`
instead. `rev` is `Reverse`, `item` is the indexer and throws `ArgumentOutOfRangeException` like it, and `indexOf` is
`IndexOf`, with the bitwise complement for a missing element. `ItemRef` has no function, because a curried function
cannot return a `byref`.
* The `FSharp.Collections.Immutable.Option.SortedSet` and `FSharp.Collections.Immutable.Tuple.SortedSet` modules repeat
`choose`, `tryGetValue` and `tryItem` with options, and `partition` and `partitionWith` with reference tuples.
* `sortedSet { }` builds a sorted set, and `SortedSetCE` created with a comparer builds sorted sets that order their
elements with it; a `null` comparer throws `ArgumentNullException`.

### Changed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Please also join the [F# Open Source Group](http://fsharp.github.com)
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | `hashSet { }` |
| `SortedSet` | `ImmutableSortedSet<'T>` | – |
| `SortedSet` | `ImmutableSortedSet<'T>` | `sortedSet { }` |
| `ISet` | `IImmutableSet<'T>` | – |
| `IIndexedSeq` | `IReadOnlyList<'T>` | – |

Expand Down
2 changes: 1 addition & 1 deletion docsSrc/Explanations/Background.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ While F# has its own built-in immutable collections, System.Collections.Immutabl
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | `hashSet { }` |
| `SortedSet` | `ImmutableSortedSet<'T>` | – |
| `SortedSet` | `ImmutableSortedSet<'T>` | `sortedSet { }` |
| `ISet` | `IImmutableSet<'T>` | – |
| `IIndexedSeq` | `IReadOnlyList<'T>` | – |

Expand Down
2 changes: 1 addition & 1 deletion docsSrc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ F# bindings for [System.Collections.Immutable](https://learn.microsoft.com/en-us
| `HashMap` | `ImmutableDictionary<'Key, 'Value>` | – |
| `SortedMap` | `ImmutableSortedDictionary<'Key, 'Value>` | – |
| `HashSet` | `ImmutableHashSet<'T>` | `hashSet { }` |
| `SortedSet` | `ImmutableSortedSet<'T>` | – |
| `SortedSet` | `ImmutableSortedSet<'T>` | `sortedSet { }` |
| `ISet` | `IImmutableSet<'T>` | – |
| `IIndexedSeq` | `IReadOnlyList<'T>` | – |

Expand Down
3 changes: 3 additions & 0 deletions src/FSharp.Collections.Immutable/ComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ let flatList = FlatListCE ()

[<GeneralizableValue>]
let hashSet<'T> = HashSetCE<'T>()

[<GeneralizableValue>]
let sortedSet<'T when 'T : comparison> = SortedSetCE<'T>()
28 changes: 28 additions & 0 deletions src/FSharp.Collections.Immutable/ComputationExpressions.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,31 @@ val flatList : FlatListCE
/// </example>
[<GeneralizableValue>]
val hashSet<'T> : HashSetCE<'T>

/// <summary>
/// Builds a sorted set that compares with F# generic comparison.
/// </summary>
/// <remarks>
/// The value is a type function, so that one value builds sorted sets of every element type. The sorted set enumerates
/// its elements in the order of the F# set of the same elements. A
/// <see cref="T:FSharp.Collections.Immutable.SortedSetCE`1"/> created with a comparer builds sorted sets that order
/// their elements with it.
/// </remarks>
/// <example id="sortedSet-1">
/// <code lang="fsharp">
/// let squares = sortedSet {
/// for i in -2..2 do
/// yield i * i
/// yield! [ 9; 4 ]
/// }
/// </code>
/// Evaluates to a sorted set of 0, 1, 4 and 9, in this order.
/// </example>
/// <example id="sortedSet-2">
/// <code lang="fsharp">
/// let names = SortedSetCE StringComparer.OrdinalIgnoreCase { "bob"; "Ann"; "ANN" }
/// </code>
/// Evaluates to a sorted set of "Ann" and "bob" that ignores case.
/// </example>
[<GeneralizableValue>]
val sortedSet<'T when 'T : comparison> : SortedSetCE<'T>
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<Compile Include="Helper\ParallelArrays.fs" />
<Compile Include="Helper\Sets.fs" />
<Compile Include="Helper\HashSets.fs" />
<Compile Include="Helper\SortedSets.fs" />
<Compile Include="FlatList\FlatListTypes.fs" />
<Compile Include="FlatList\FlatList.fsi" />
<Compile Include="FlatList\FlatList.fs" />
Expand Down Expand Up @@ -67,12 +68,26 @@
<Compile Include="HashSet\ISetTuple.fs" />
<Compile Include="HashSet\HashSetCE.fsi" />
<Compile Include="HashSet\HashSetCE.fs" />
<Compile Include="SortedSet\SortedSetTypes.fs" />
<Compile Include="SortedSet\SortedSet.CustomComparer.fsi">
<DependentUpon>SortedSet.fsi</DependentUpon>
</Compile>
<Compile Include="SortedSet\SortedSet.CustomComparer.fs">
<DependentUpon>SortedSet.fs</DependentUpon>
</Compile>
<Compile Include="SortedSet\SortedSet.fsi" />
<Compile Include="SortedSet\SortedSet.fs" />
<Compile Include="SortedSet\SortedSetOption.fsi" />
<Compile Include="SortedSet\SortedSetOption.fs" />
<Compile Include="SortedSet\SortedSetTuple.fsi" />
<Compile Include="SortedSet\SortedSetTuple.fs" />
<Compile Include="SortedSet\SortedSetCE.fsi" />
<Compile Include="SortedSet\SortedSetCE.fs" />
<Compile Include="Stack.fs" />
<Compile Include="ImmutableList.fs" />
<Compile Include="Queue.fs" />
<Compile Include="IndexedSeq.fs" />
<Compile Include="Maps.fs" />
<Compile Include="Sets.fs" />
<Compile Include="ComputationExpressions.fsi" />
<Compile Include="ComputationExpressions.fs" />
</ItemGroup>
Expand Down
4 changes: 4 additions & 0 deletions src/FSharp.Collections.Immutable/Helper/ErrorStrings.fs
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@ let NotEnoughElements = "The input sequence has an insufficient number of elemen
/// FSCore.resx, outOfRange.
[<Literal>]
let OutOfRange = "The index is outside the legal range."

/// FSCore.resx, setContainsNoElements.
[<Literal>]
let SetContainsNoElements = "Set contains no elements."
76 changes: 76 additions & 0 deletions src/FSharp.Collections.Immutable/Helper/SortedSets.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/// <summary>
/// Algorithms that build sorted sets one element at a time, for the creation members of <c>CustomComparerSortedSet</c>
/// and the transformations of the <c>SortedSet</c> module that change the element type (ADR 0001, D5).
/// </summary>
/// <remarks>
/// <para>
/// A builder keeps the first of the elements that compare equal, as the FSharp.Core <c>Set</c> module does.
/// <c>ImmutableSortedSet.CreateRange</c> sorts the elements with an unstable sort before it drops the duplicates, so it can
/// keep any of them.
/// </para>
/// <para>
/// The transformations that change the element type compare the new elements with F# generic comparison,
/// <c>DefaultComparer</c> of the <c>Comparers</c> module, the comparer of the sorted sets created without one (D5,
/// Comparers): the comparer of the input cannot compare elements of another type.
/// </para>
/// </remarks>
module internal FSharp.Collections.Immutable.SortedSets

open System.Collections.Generic
open System.Collections.Immutable

/// <summary>
/// The distinct elements of a sequence in a sorted set with the comparer, the first of the elements that compare equal.
/// </summary>
/// <remarks>
/// The caller checks the input: <c>checkNotNullOrDefaultSeq</c> for a sequence, which also rejects a default flat list
/// given as a sequence, and <c>checkNotNull</c> for an array or a list. The exception then names the parameter of the
/// caller and not the parameter of <c>UnionWith</c> (ADR 0001, D6).
/// </remarks>
let ofSeq (comparer : IComparer<'T>) (source : seq<'T>) : ImmutableSortedSet<'T> =
let builder = ImmutableSortedSet.CreateBuilder<'T> comparer
builder.UnionWith source
builder.ToImmutable ()

/// The values that the chooser returns for the elements.
let inline choose ([<InlineIfLambda>] chooser : 'T -> 'U voption) (source : ImmutableSortedSet<'T>) : ImmutableSortedSet<'U> =
let builder = ImmutableSortedSet.CreateBuilder<'U> DefaultComparer<'U>.Instance

for item in source do
match chooser item with
| ValueSome value -> builder.Add value |> ignore
| ValueNone -> ()

builder.ToImmutable ()

/// The results of the mapping of the elements.
let inline map ([<InlineIfLambda>] mapping : 'T -> 'U) (source : ImmutableSortedSet<'T>) : ImmutableSortedSet<'U> =
let builder = ImmutableSortedSet.CreateBuilder<'U> DefaultComparer<'U>.Instance

for item in source do
builder.Add (mapping item) |> ignore

builder.ToImmutable ()

/// <summary>
/// The values of the first choices and the values of the second choices that the partitioner returns for the elements,
/// which it visits in descending order.
/// </summary>
/// <remarks>
/// <c>Set.partitionWith</c> visits the elements from the largest one and keeps the first of the values that compare
/// equal, and so does a builder: the same one of two equal values stays.
/// </remarks>
let inline partitionWith
([<InlineIfLambda>] partitioner : 'T -> Choice<'T1, 'T2>)
(source : ImmutableSortedSet<'T>)
: struct (ImmutableSortedSet<'T1> * ImmutableSortedSet<'T2>) =
let first = ImmutableSortedSet.CreateBuilder<'T1> DefaultComparer<'T1>.Instance
let second = ImmutableSortedSet.CreateBuilder<'T2> DefaultComparer<'T2>.Instance

// Reverse walks the tree from the largest element without a copy
for item in source.Reverse () do
match partitioner item with
| Choice1Of2 value -> first.Add value |> ignore
| Choice2Of2 value -> second.Add value |> ignore

struct (first.ToImmutable (), second.ToImmutable ())
131 changes: 0 additions & 131 deletions src/FSharp.Collections.Immutable/Sets.fs

This file was deleted.

Loading