Generic Record Types have different Equality semantics to concrete types
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 131
Description
From #513
The problem is that for concrete types, the implementation of IStructuralEquatable optimizes standard types, which means that the IEqualityComparer is unused.
The following example shows this by using a specialized IEqualityComparer to try to do an uppercase string comparison.
```
type A = { A : string }
type B<'a> = { B : 'a }
[]
let main argv =
let a1, a2 = { A = "Hello"}, { A = "HELLO" }
let b1, b2 = { B = "Hello"}, { B = "HELLO" }
let compareStringWithUpper=
let toupper (s:string) = s.ToUpper ()
{ new IEqualityComparer with
member this.GetHashCode item =
match item with
| :? string as s -> (toupper s).GetHashCode ()
| _ -> failwith "Not in this example..."
member this.Equals (lhs, rhs) =
match lhs, rhs with
| (:? string as s1), (:? string as s2) -> (toupper s1).Equals(toupper s2)
| _ -> failwith "Not in this example..." }
let a_is_good = (a1 :> IStructuralEquatable).Equals(a2, compareStringWithUpper)
let b_is_good = (b1 :> IStructuralEquatable).Equals(b2, compareStringWithUpper)
printfn "A is %s" <| if a_is_good then "Good" else "Bad"
printfn "B is %s" <| if b_is_good then "Good" else "Bad"
if a_is_good <> b_is_good then
printfn "But worse than those results are that they are inconsistent!"
```
With the output
```
A is Bad
B is Good
But worse than those results are that they are inconsistent!
```
I think the implementation of IStructuralEquatable.Equals shouldn't used inlined IL, but rather always defer back to supplied IEqualityComparer.
Fixing this before #513 is implemented would have a performance impact.
Contributor guide
Assessment
This issue has not been assessed yet.