julia> x, y = Dict(1 => 2), Dict(1 => 2)
(Dict(1=>2), Dict(1=>2))
julia> x == y
true
julia> keys(x) == keys(y) # falls back to ===
false
This is a specific example of a more general phenomenon I've noticed where the meaning of == ("Should be implemented for all types with a notion of equality, based on the abstract value that an instance represents") is seemingly disobeyed:
julia> (x for x in 1:3) == (x for x in 1:3)
false
julia> zip([1,2],[3,4]) == zip([1,2],[3,4])
false
With regards to 1.0 stability, would changing the result of == in cases like this constitute a bugfix or a breaking change?
Oh dear... in #24580 I switched this to an AbstractSet and probably should have checked out if that interface provided functions like this (like AbstractArray does).
I consider this a bug (is it a regression?) for ==(::KeySet, ::KeySet) since ==(::AbstractDict, ::AbstractDict) is also unordered, and IMO this method could be implemented now.
Another highly related issue is #25318. I'd suggest implementing a generic issetequal(a, b) (or whatever you want to call that) on all iterables, add fast specializations, and ==(a::AbstractSet, b::AbstractSet) = issetequal(a, b). Again, I think this would be helpful to get into v1.0 because we'd want to avoid breaking changes to == in v1.x.
and ==(a::AbstractSet, b::AbstractSet) = issetequal(a, b).
This is what I initially did in #25368, but Jeff suggested that the primary implementation that a set type implementation should specialize is ==, not issetequal.
Right; calling issetequal should only be necessary if the arguments are enough unlike sets that == doesn't already do what you want.
We should add == for KeySet. Generic ==(a::AbstractSet, b::AbstractSet) also sounds good but I imagine it should use in (and maybe length?)
Also related: #4648
Fixed for KeySet by #25368.
Now all that's left is to handle (x->x) == (x->x) :-P
Closing as dup of #4648
Closing as dup of #4648
This is a case where #4648 would not be helpful... we want keys(Dict(1=>1)) == keys(Dict(1=>100)) but recursive == would just give false because it would compare the entire dictionaries (keys and values).
Anyway, yes this should be closed.
Most helpful comment
Now all that's left is to handle
(x->x) == (x->x):-P