-
Notifications
You must be signed in to change notification settings - Fork 822
List.Equals
performs full structural comparison for the same object.
#14672
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
Comments
List.Equals
performs full structural comparison for the same object.
This has been a point of discussion in the LISP, SML, OCaml, ... communities from the day I joined them :) It will still be being discussed in 3022 I'm sure. For many practical purposes it is wise to add shortcut equality to an equality algorithm and other traversals (e.g. we use pointer equality in many places in the compiler). However, adding it by default for a generic structural equality can make the structural equality semantically "dubious" (e.g. unreliable, subtle) and performance very sensitive to code changes. For example, we generally expect
and
to give the same result. But it's possible to have objects where "x.Equals(x)" will return false, raise an exception or not terminate. So adding shortcutting makes equality return true more often. Further in F#, some object identity is "up to the compiler", e.g. allocations of closures, tuples or, potentially, static constant lists like The arguments about this go back a very long way and there's no really perfect position. We decided in F# 1/2 that we didn't want optimization to affect execution wherever possible - unless you explicitly opt-in to using Object.ReferenceEquals or equivalent - and there's something in language spec about that. So all in all I think this is in the category of "decided in F# 2.0" - we chose a point on the spectrum, fixed it, and that's that - changing it would help performance in some situations but it might be better to simply write a good guide to equality in the F# docs. The topic is big and important one and could cover things like how to implement your own equality (both explicit and .Equals), what the expected semantics should be for different kinds of equality, how and when to use Object.ReferenceEquals. |
So I will close this as by design. For reference - for those who are just diving into this topic, here is an example to illustrate what Don's saying: open System
type MyReferenceType() =
member x.Value = "Some Value"
override x.Equals(y) =
Object.ReferenceEquals(x, y)
override x.GetHashCode() =
System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(x)
let a = MyReferenceType()
let b = MyReferenceType()
// Comparison using structural equality (but overridden to be reference equality)
let result1 = ([a] = [b])
// Comparison using the same instance
let xs = [a]
let result2 = (xs = xs)
printfn "Result 1 (Different instances): %b" result1 // false
printfn "Result 2 (Same instance): %b" result2 // true |
Uh oh!
There was an error while loading. Please reload this page.
F# list's
List.Equals
always performs full structural comparison, even if the two objects being compared are the same object.Repro steps
Expected behavior
List and array-based code should be equally quick.
Actual behavior
List is much slower:
Known workarounds
List.Equals
Related information
#9348
#6175
The text was updated successfully, but these errors were encountered: