Repository navigation
feat: add FSharp.Azure.Cosmos.Sql with EquatableArray and the ImmutableArray patterns
#48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
xperiandri
wants to merge
1
commit into
main
Choose a base branch
from
feat/cosmos-sql-equatable-array
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // Auto-Generated by FAKE; do not edit | ||
| namespace System | ||
| open System.Reflection | ||
|
|
||
| [<assembly: AssemblyTitleAttribute("FSharp.Azure.Cosmos.Sql")>] | ||
| [<assembly: AssemblyProductAttribute("FSharp.Azure.Cosmos")>] | ||
| [<assembly: AssemblyVersionAttribute("1.2.0")>] | ||
| [<assembly: AssemblyMetadataAttribute("ReleaseDate","2026-10-01T00:00:00.0000000+02:00")>] | ||
| [<assembly: AssemblyFileVersionAttribute("1.2.0")>] | ||
| [<assembly: AssemblyInformationalVersionAttribute("1.2.0")>] | ||
| [<assembly: AssemblyMetadataAttribute("ReleaseChannel","release")>] | ||
| [<assembly: AssemblyMetadataAttribute("GitHash","210cf041a0ce44fcc5a5df369550a77a1f75d436")>] | ||
| do () | ||
|
|
||
| module internal AssemblyVersionInformation = | ||
| let [<Literal>] AssemblyTitle = "FSharp.Azure.Cosmos.Sql" | ||
| let [<Literal>] AssemblyProduct = "FSharp.Azure.Cosmos" | ||
| let [<Literal>] AssemblyVersion = "1.2.0" | ||
| let [<Literal>] AssemblyMetadata_ReleaseDate = "2026-10-01T00:00:00.0000000+02:00" | ||
| let [<Literal>] AssemblyFileVersion = "1.2.0" | ||
| let [<Literal>] AssemblyInformationalVersion = "1.2.0" | ||
| let [<Literal>] AssemblyMetadata_ReleaseChannel = "release" | ||
| let [<Literal>] AssemblyMetadata_GitHash = "210cf041a0ce44fcc5a5df369550a77a1f75d436" |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| namespace FSharp.Azure.Cosmos.Sql | ||
|
|
||
| open System | ||
| open System.Collections | ||
| open System.Collections.Generic | ||
| open System.Collections.Immutable | ||
| open System.Runtime.InteropServices | ||
|
|
||
| /// <summary> | ||
| /// An immutable array with structural equality: two instances are equal when they hold equal elements in the same | ||
| /// order, and the hash code combines the hash codes of the elements. | ||
| /// <para> | ||
| /// The own equality of <see cref="T:System.Collections.Immutable.ImmutableArray`1"/>, its | ||
| /// <see cref="M:System.Collections.Immutable.ImmutableArray`1.Equals(System.Collections.Immutable.ImmutableArray{`0})"/> | ||
| /// that <see cref="P:System.Collections.Generic.EqualityComparer`1.Default"/> calls, compares the reference of the array | ||
| /// it wraps. Only comparers that go through <see cref="T:System.Collections.IStructuralEquatable"/>, such as the equality | ||
| /// the F# compiler generates for records and unions, compare its elements; | ||
| /// <see cref="M:System.ValueTuple`2.Equals(System.ValueTuple{`0,`1})"/>, a | ||
| /// <see cref="T:System.Collections.Generic.HashSet`1"/> of arrays or a C# caller compares references, and a default | ||
| /// array never equals an empty one. The nodes of the syntax tree hold their child sequences in this wrapper instead | ||
| /// (the pattern Roslyn incremental generators use), so their equality is structural under every comparer, for | ||
| /// round-trip tests, cache keys and the idempotence of passes. | ||
| /// </para> | ||
| /// <para> | ||
| /// The default value holds no array; it behaves as an empty array and is equal to one. | ||
| /// </para> | ||
| /// </summary> | ||
| /// <param name="array">The elements, in order.</param> | ||
| [<Struct; CustomEquality; NoComparison>] | ||
| type EquatableArray<'T> (array : ImmutableArray<'T>) = | ||
|
|
||
| /// <summary> | ||
| /// The elements; never a default <see cref="T:System.Collections.Immutable.ImmutableArray`1"/>, so it can be | ||
| /// enumerated and indexed without a check. | ||
| /// </summary> | ||
| member _.Items = if array.IsDefault then ImmutableArray<'T>.Empty else array | ||
|
|
||
| /// The number of elements. | ||
| member _.Length = if array.IsDefault then 0 else array.Length | ||
|
|
||
| /// Whether the array has no elements. | ||
| member this.IsEmpty = this.Length = 0 | ||
|
|
||
| /// <summary> | ||
| /// The element at <paramref name="index"/>. | ||
| /// </summary> | ||
| /// <param name="index">The zero-based position of the element.</param> | ||
| member this.Item | ||
| with get (index : int) = this.Items[index] | ||
|
|
||
| /// <summary> | ||
| /// Returns an allocation-free enumerator over the elements, which F# <see langword="for"/> loops use. | ||
| /// </summary> | ||
| member this.GetEnumerator () = this.Items.GetEnumerator () | ||
|
|
||
| /// <summary> | ||
| /// Whether <paramref name="other"/> holds equal elements in the same order; elements are compared with | ||
| /// <see cref="P:System.Collections.Generic.EqualityComparer`1.Default"/>. | ||
| /// </summary> | ||
| /// <param name="other">The array to compare with.</param> | ||
| member this.Equals (other : EquatableArray<'T>) = | ||
| let left = this.Items | ||
| let right = other.Items | ||
|
|
||
| if left.Length <> right.Length then | ||
| false | ||
| else | ||
| let comparer = EqualityComparer<'T>.Default | ||
| let mutable equal = true | ||
| let mutable index = 0 | ||
|
|
||
| while equal && index < left.Length do | ||
| equal <- comparer.Equals (left[index], right[index]) | ||
| index <- index + 1 | ||
|
|
||
| equal | ||
|
|
||
| /// <inheritdoc /> | ||
| override this.Equals (other : objnull) = | ||
| match other with | ||
| | :? EquatableArray<'T> as other -> this.Equals other | ||
| | _ -> false | ||
|
|
||
| /// Combines the hash codes of the elements, in order, so equal arrays have equal hash codes. | ||
| override this.GetHashCode () = | ||
| let mutable hash = HashCode () | ||
|
|
||
| for item in this.Items do | ||
| hash.Add item | ||
|
|
||
| hash.ToHashCode () | ||
|
|
||
| /// Shows the elements in square brackets, separated by semicolons, the way F# shows an array. | ||
| override this.ToString () = | ||
| let items = this.Items |> Seq.map (fun item -> $"%A{item}") | ||
| $"""[| {String.Join ("; ", items)} |]""" | ||
|
|
||
| /// <summary> | ||
| /// Wraps <paramref name="items"/> without copying it, so that an | ||
| /// <see cref="T:System.Collections.Immutable.ImmutableArray`1"/> can be given where an | ||
| /// <see cref="T:FSharp.Azure.Cosmos.Sql.EquatableArray`1"/> is expected. | ||
| /// </summary> | ||
| /// <param name="items">The elements.</param> | ||
| static member op_Implicit (items : ImmutableArray<'T>) : EquatableArray<'T> = EquatableArray items | ||
|
|
||
| /// <summary> | ||
| /// The elements of <paramref name="array"/> as an <see cref="T:System.Collections.Immutable.ImmutableArray`1"/>, | ||
| /// without copying them and never as a default array, so that an | ||
| /// <see cref="T:FSharp.Azure.Cosmos.Sql.EquatableArray`1"/> can be given where an | ||
| /// <see cref="T:System.Collections.Immutable.ImmutableArray`1"/> is expected. | ||
| /// </summary> | ||
| /// <param name="array">The array.</param> | ||
| static member op_Implicit (array : EquatableArray<'T>) : ImmutableArray<'T> = array.Items | ||
|
|
||
| interface IEquatable<EquatableArray<'T>> with | ||
| /// <inheritdoc /> | ||
| member this.Equals other = this.Equals other | ||
|
|
||
| interface IReadOnlyList<'T> with | ||
| /// <inheritdoc /> | ||
| member this.Count = this.Length | ||
|
|
||
| /// <inheritdoc /> | ||
| member this.Item | ||
| with get index = this.Items[index] | ||
|
|
||
| /// <inheritdoc /> | ||
| member this.GetEnumerator () : IEnumerator<'T> = (this.Items :> IEnumerable<'T>).GetEnumerator() | ||
|
|
||
| /// <inheritdoc /> | ||
| member this.GetEnumerator () : IEnumerator = (this.Items :> IEnumerable).GetEnumerator() | ||
|
|
||
| /// <summary> | ||
| /// Functions that create and transform <see cref="T:FSharp.Azure.Cosmos.Sql.EquatableArray`1"/> values. | ||
| /// </summary> | ||
| [<RequireQualifiedAccess>] | ||
| [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] | ||
| module EquatableArray = | ||
|
|
||
| /// The array without elements. | ||
| [<GeneralizableValue>] | ||
| let empty<'T> : EquatableArray<'T> = EquatableArray ImmutableArray<'T>.Empty | ||
|
|
||
| /// <summary> | ||
| /// Wraps <paramref name="items"/> without copying it. | ||
| /// </summary> | ||
| /// <param name="items">The elements.</param> | ||
| let ofImmutableArray (items : ImmutableArray<'T>) = EquatableArray items | ||
|
|
||
| /// <summary> | ||
| /// Copies <paramref name="items"/> into a new array. | ||
| /// </summary> | ||
| /// <param name="items">The elements.</param> | ||
| let ofArray (items : 'T array) = EquatableArray (ImmutableArray.Create<'T> items) | ||
|
|
||
| /// <summary> | ||
| /// Wraps <paramref name="items"/> without copying it, as | ||
| /// <see cref="M:System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray``1(``0[])"/> does. | ||
| /// <para> | ||
| /// The caller gives the array away: the result is immutable only as long as nothing writes to | ||
| /// <paramref name="items"/> any more. A later write changes the elements, the equality and the hash code of the | ||
| /// result, also where it already is a key of a dictionary or a member of a set. | ||
| /// </para> | ||
| /// </summary> | ||
| /// <param name="items">The elements, in an array that is never written to again.</param> | ||
| let unsafeOfArray (items : 'T array) = EquatableArray (ImmutableCollectionsMarshal.AsImmutableArray<'T> items) | ||
|
|
||
| /// <summary> | ||
| /// Copies <paramref name="items"/> into a new array. | ||
| /// </summary> | ||
| /// <param name="items">The elements.</param> | ||
| let ofSeq (items : 'T seq) = EquatableArray (ImmutableArray.CreateRange<'T> items) | ||
|
|
||
| /// <summary> | ||
| /// An array that holds <paramref name="item"/> only. | ||
| /// </summary> | ||
| /// <param name="item">The element.</param> | ||
| let singleton (item : 'T) = EquatableArray (ImmutableArray.Create<'T> item) | ||
|
|
||
| /// <summary> | ||
| /// The elements of <paramref name="array"/> as an <see cref="T:System.Collections.Immutable.ImmutableArray`1"/>, | ||
| /// without copying them. | ||
| /// </summary> | ||
| /// <param name="array">The array.</param> | ||
| let toImmutableArray (array : EquatableArray<'T>) = array.Items | ||
|
|
||
| /// <summary> | ||
| /// Applies <paramref name="mapping"/> to every element of <paramref name="array"/>, in order. | ||
| /// </summary> | ||
| /// <param name="mapping">The function to apply.</param> | ||
| /// <param name="array">The array.</param> | ||
| let map (mapping : 'T -> 'U) (array : EquatableArray<'T>) = | ||
| let items = array.Items | ||
| let builder = ImmutableArray.CreateBuilder<'U> items.Length | ||
|
|
||
| for item in items do | ||
| builder.Add (mapping item) | ||
|
|
||
| EquatableArray (builder.MoveToImmutable ()) | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <AssemblyName>$(AssemblyBaseName).Sql</AssemblyName> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| <Deterministic>true</Deterministic> | ||
| <!-- Not packed until the syntax tree, the printer and the validator are in: the collection types alone are no package --> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <PropertyGroup Condition="'$(Configuration)'=='Release'"> | ||
| <Optimize>true</Optimize> | ||
| <Tailcalls>true</Tailcalls> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="AssemblyInfo.fs" /> | ||
| <Compile Include="EquatableArray.fs" /> | ||
| <Compile Include="ImmutableArrayPatterns.fs" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.SourceLink.GitHub"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| /// <summary> | ||
| /// Struct partial active patterns that match an <see cref="T:System.Collections.Immutable.ImmutableArray`1"/> by its | ||
| /// length, so code over the children of a syntax node reads like a list pattern without converting to an F# list. | ||
| /// <para> | ||
| /// Each pattern returns a value option made a struct by <see cref="T:Microsoft.FSharp.Core.StructAttribute"/> on its | ||
| /// return value, or a <see cref="T:System.Boolean"/>, and its payload is a struct tuple, so a match allocates nothing. | ||
| /// A multi-case active pattern over the lengths would return a heap | ||
| /// <see cref="T:Microsoft.FSharp.Core.FSharpChoice`3"/> on every match, which is why every length is its own partial | ||
| /// pattern and lengths a pattern does not cover fall through to the next case. | ||
| /// </para> | ||
| /// </summary> | ||
| /// <example> | ||
| /// <code lang="fsharp"> | ||
| /// match arguments with | ||
| /// | Arr0 -> "no argument" | ||
| /// | Arr1 single -> "one argument" | ||
| /// | Arr2 (first, second) -> "two arguments" | ||
| /// | Arr3 (first, second, third) -> "three arguments" | ||
| /// | ArrN 4 -> "four arguments" | ||
| /// | _ -> "more arguments" | ||
| /// </code> | ||
| /// </example> | ||
| [<AutoOpen>] | ||
| module FSharp.Azure.Cosmos.Sql.ImmutableArrayPatterns | ||
|
|
||
| open System.Collections.Immutable | ||
|
|
||
| /// <summary> | ||
| /// Matches an array without elements. A default (uninitialized) array counts as empty. | ||
| /// </summary> | ||
| /// <param name="items">The array to match.</param> | ||
| let (|Arr0|_|) (items : ImmutableArray<'T>) = items.IsDefaultOrEmpty | ||
|
|
||
| /// <summary> | ||
| /// Matches an array of exactly one element and returns that element. | ||
| /// </summary> | ||
| /// <param name="items">The array to match.</param> | ||
| [<return : Struct>] | ||
| let (|Arr1|_|) (items : ImmutableArray<'T>) = | ||
| if not items.IsDefault && items.Length = 1 then | ||
| ValueSome items[0] | ||
| else | ||
| ValueNone | ||
|
|
||
| /// <summary> | ||
| /// Matches an array of exactly two elements and returns them as a struct tuple, which the pattern binds with or | ||
| /// without the <see langword="struct"/> keyword. | ||
| /// </summary> | ||
| /// <param name="items">The array to match.</param> | ||
| [<return : Struct>] | ||
| let (|Arr2|_|) (items : ImmutableArray<'T>) = | ||
| if not items.IsDefault && items.Length = 2 then | ||
| ValueSome (struct (items[0], items[1])) | ||
| else | ||
| ValueNone | ||
|
|
||
| /// <summary> | ||
| /// Matches an array of exactly three elements and returns them as a struct tuple. | ||
| /// </summary> | ||
| /// <param name="items">The array to match.</param> | ||
| [<return : Struct>] | ||
| let (|Arr3|_|) (items : ImmutableArray<'T>) = | ||
| if not items.IsDefault && items.Length = 3 then | ||
| ValueSome (struct (items[0], items[1], items[2])) | ||
| else | ||
| ValueNone | ||
|
|
||
| /// <summary> | ||
| /// Matches an array of exactly <paramref name="length"/> elements, for the lengths that have no pattern of their own; | ||
| /// index the array to read its elements. | ||
| /// </summary> | ||
| /// <param name="length">The number of elements to match, written as the argument of the pattern.</param> | ||
| /// <param name="items">The array to match.</param> | ||
| let (|ArrN|_|) (length : int) (items : ImmutableArray<'T>) = (if items.IsDefault then 0 else items.Length) = length |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.