From fa2b24abe2dd5212dab08eefb1764a606972a22f Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 28 Apr 2020 15:01:43 -0700 Subject: [PATCH 1/3] Custom equality and comparison for F# lists --- src/fsharp/FSharp.Core/prim-types.fs | 59 ++++++++++++++++++++++++++- src/fsharp/FSharp.Core/prim-types.fsi | 3 +- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 78441e75df8..24aab108f0a 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -3004,6 +3004,7 @@ namespace Microsoft.FSharp.Collections // Lists //------------------------------------------------------------------------- + open System open System.Collections.Generic open System.Diagnostics open Microsoft.FSharp.Core @@ -3016,7 +3017,7 @@ namespace Microsoft.FSharp.Collections [>)>] [] [] - [] + [] [] type List<'T> = | ([]) : 'T list @@ -3025,6 +3026,7 @@ namespace Microsoft.FSharp.Collections interface System.Collections.IEnumerable interface System.Collections.Generic.IReadOnlyCollection<'T> interface System.Collections.Generic.IReadOnlyList<'T> + interface IComparable and 'T list = List<'T> @@ -3186,6 +3188,7 @@ namespace Microsoft.FSharp.Collections static member Empty : 'T list = [] static member Cons(head,tail) : 'T list = head :: tail + override x.ToString() = match x with | [] -> "[]" @@ -3206,6 +3209,41 @@ namespace Microsoft.FSharp.Collections [] member l.GetReverseIndex(_: int, offset: int) = l.Length - offset - 1 + + override this.GetHashCode() = + let len = PrivateListHelpers.lengthAcc 0 this + let mutable i = len - 1 + if i > LanguagePrimitives.HashCompare.defaultHashNodes then + i <- LanguagePrimitives.HashCompare.defaultHashNodes // Limit the hash + + let rec loop l i acc = + match l with + | [] -> acc + | h::t -> + if i >= 0 then + loop t (i - 1) (LanguagePrimitives.HashCompare.HashCombine i acc (LanguagePrimitives.GenericHash h)) + else + acc + + loop this i 0 + + override this.Equals(other) = + let rec loop x y = + match x, y with + | [], [] -> true + | _, [] -> false + | [], _ -> false + | xHead :: xTail, yHead :: yTail -> + if not (GenericEquality xHead yHead) then + false + else + loop xTail yTail + + match other with + | :? List<'T> as o -> + loop this o + | _ -> + false interface IEnumerable<'T> with member l.GetEnumerator() = PrivateListHelpers.mkListEnumerator l @@ -3219,6 +3257,25 @@ namespace Microsoft.FSharp.Collections interface IReadOnlyList<'T> with member l.Item with get(index) = l.[index] + interface IComparable with + member this.CompareTo(other) = + let rec loop l1 l2 = + match l1, l2 with + | [], [] -> 0 + | _, [] -> 1 + | [], _ -> -1 + | h1 :: t1, h2 :: t2 -> + let res = GenericComparison h1 h2 + if res <> 0 then + res + else + loop t1 t2 + + match other with + | :? List<'T> as o -> + loop this o + | _ -> 0 + type seq<'T> = IEnumerable<'T> diff --git a/src/fsharp/FSharp.Core/prim-types.fsi b/src/fsharp/FSharp.Core/prim-types.fsi index afa0719602c..39bcf8516f3 100644 --- a/src/fsharp/FSharp.Core/prim-types.fsi +++ b/src/fsharp/FSharp.Core/prim-types.fsi @@ -1898,7 +1898,7 @@ namespace Microsoft.FSharp.Collections /// the notation [1;2;3]. Use the values in the List module to manipulate /// values of this type, or pattern match against the values directly. [] - [] + [] [] type List<'T> = | ([]) : 'T list @@ -1948,6 +1948,7 @@ namespace Microsoft.FSharp.Collections interface IEnumerable interface IReadOnlyCollection<'T> interface IReadOnlyList<'T> + interface IComparable /// An abbreviation for the type of immutable singly-linked lists. /// From 6c14e53ed44cd8d9577801eed8e38d6a556ad842 Mon Sep 17 00:00:00 2001 From: cartermp Date: Tue, 28 Apr 2020 17:55:55 -0700 Subject: [PATCH 2/3] Use GenericEqualityER since that's what the generated equality would use --- src/fsharp/FSharp.Core/prim-types.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 24aab108f0a..55ba48c20f5 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -3234,7 +3234,7 @@ namespace Microsoft.FSharp.Collections | _, [] -> false | [], _ -> false | xHead :: xTail, yHead :: yTail -> - if not (GenericEquality xHead yHead) then + if not (GenericEqualityER xHead yHead) then false else loop xTail yTail From ca4463531cff2fc5451a0a5b9226f5e099dfd149 Mon Sep 17 00:00:00 2001 From: cartermp Date: Thu, 4 Jun 2020 15:55:45 -0700 Subject: [PATCH 3/3] Finish up the explicit interface impls --- src/fsharp/FSharp.Core/prim-types.fs | 89 +++++++++++++++++++++++++++ src/fsharp/FSharp.Core/prim-types.fsi | 4 ++ 2 files changed, 93 insertions(+) diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs index 55ba48c20f5..7ce13cd5a62 100644 --- a/src/fsharp/FSharp.Core/prim-types.fs +++ b/src/fsharp/FSharp.Core/prim-types.fs @@ -3005,6 +3005,7 @@ namespace Microsoft.FSharp.Collections //------------------------------------------------------------------------- open System + open System.Collections open System.Collections.Generic open System.Diagnostics open Microsoft.FSharp.Core @@ -3027,6 +3028,10 @@ namespace Microsoft.FSharp.Collections interface System.Collections.Generic.IReadOnlyCollection<'T> interface System.Collections.Generic.IReadOnlyList<'T> interface IComparable + interface IComparable> + interface IEquatable> + interface IStructuralComparable + interface IStructuralEquatable and 'T list = List<'T> @@ -3276,6 +3281,90 @@ namespace Microsoft.FSharp.Collections loop this o | _ -> 0 + interface IComparable> with + member this.CompareTo(other) = + let rec loop l1 l2 = + match l1, l2 with + | [], [] -> 0 + | _, [] -> 1 + | [], _ -> -1 + | h1 :: t1, h2 :: t2 -> + let res = GenericComparison h1 h2 + if res <> 0 then + res + else + loop t1 t2 + loop this other + + interface IEquatable> with + member this.Equals(other) = + let rec loop x y = + match x, y with + | [], [] -> true + | _, [] -> false + | [], _ -> false + | xHead :: xTail, yHead :: yTail -> + if not (GenericEqualityER xHead yHead) then + false + else + loop xTail yTail + + loop this other + + interface IStructuralComparable with + member this.CompareTo(other, comparer) = + let rec loop l1 l2 = + match l1, l2 with + | [], [] -> 0 + | _, [] -> 1 + | [], _ -> -1 + | h1 :: t1, h2 :: t2 -> + let res = comparer.Compare(h1, h2) + if res <> 0 then + res + else + loop t1 t2 + + match other with + | :? List<'T> as o -> + loop this o + | _ -> 0 + + interface IStructuralEquatable with + member this.Equals(other, comparer) = + let rec loop x y = + match x, y with + | [], [] -> true + | _, [] -> false + | [], _ -> false + | xHead :: xTail, yHead :: yTail -> + if not (comparer.Equals(xHead, yHead)) then + false + else + loop xTail yTail + + match other with + | :? List<'T> as o -> + loop this o + | _ -> false + + member this.GetHashCode(comparer) = + let len = PrivateListHelpers.lengthAcc 0 this + let mutable i = len - 1 + if i > LanguagePrimitives.HashCompare.defaultHashNodes then + i <- LanguagePrimitives.HashCompare.defaultHashNodes // Limit the hash + + let rec loop l i acc = + match l with + | [] -> acc + | h::t -> + if i >= 0 then + loop t (i - 1) (LanguagePrimitives.HashCompare.HashCombine i acc (comparer.GetHashCode(h))) + else + acc + + loop this i 0 + type seq<'T> = IEnumerable<'T> diff --git a/src/fsharp/FSharp.Core/prim-types.fsi b/src/fsharp/FSharp.Core/prim-types.fsi index 39bcf8516f3..fd5f7a0e377 100644 --- a/src/fsharp/FSharp.Core/prim-types.fsi +++ b/src/fsharp/FSharp.Core/prim-types.fsi @@ -1949,6 +1949,10 @@ namespace Microsoft.FSharp.Collections interface IReadOnlyCollection<'T> interface IReadOnlyList<'T> interface IComparable + interface IComparable> + interface IEquatable> + interface IStructuralComparable + interface IStructuralEquatable /// An abbreviation for the type of immutable singly-linked lists. ///