A dictionary in my code is not working because keys that are supposed to be equal are comparing as unequal. Here is a snippet (Julia 1.8.2). Is this a bug, or am I misunderstanding how == is supposed to work?
julia> abstract type ET end
julia> struct ET1 <: ET
end
julia> struct SK1{T <: ET}
nl::Vector{Int}
end
julia> k1 = SK1{ET1}([4,3,5])
SK1{ET1}([4, 3, 5])
julia> k2 = SK1{ET1}([4,3,5])
SK1{ET1}([4, 3, 5])
julia> k1 == k2
false
And to expand on oscars answer, this is what the docstring for === says
help?> ===
search: === == !==
===(x,y) -> Bool
≡(x,y) -> Bool
Determine whether x and y are identical, in the sense that no program could
distinguish them. First the types of x and y are compared. If those are identical,
mutable objects are compared by address in memory and immutable objects (such as
numbers) are compared by contents at the bit level. This function is sometimes
called "egal". It always returns a Bool value.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> a = [1, 2]; b = [1, 2];
julia> a == b
true
julia> a === b
false
julia> a === a
true
so it makes sense since the objects are not the same, they just contain the same values.
If you want to check if they contain the same value, you could compare the content instead
k1.nl == k2.nl
or you could overload == for your type if want to directly compare them
Sorry, I’m not getting it yet; here are some followup questions.
The first response from Oscar_Smith, which appears to correctly describe the behavior of Julia according to my tests, is inconsistent with the docstring. Here is an excerpt from the docstring:
help?> ==
[snip]
For collections, == is generally
called recursively on all contents, though other properties (like the shape for arrays) may also be taken into
account.
So in other words, the docstring seems to say that == should be called on the nl field, not ===.
My actual requirement is to use SK as a dict key, and I haven’t gotten this to work yet. Below is my attempt to make this work. I was expecting the output of the final four @show statements to be 8, 8, 9, 10, but instead I got 7,8,9,10 when I ran the code below, so I am still making some mistake.
Thanks
abstract type E end
import Base.==
import Base.hash
struct E1 <: E
end
struct E2 <: E
end
struct SK{T <: E}
nl::Vector{Int}
end
getparamtype(sk::SK{T}) where T = T
==(sk1::SK, sk2::SK) = getparamtype(sk1) === getparamtype(sk2) && sk1.nl == sk2.nl
hash(sk::SK,h::Int) = hash((hash(getparamtype(sk),h),hash(sk.nl,h)),h)
a = SK{E1}([4,3,5])
b = SK{E1}([4,3,5])
c = SK{E1}([4,3,4])
d = SK{E2}([4,3,5])
@show a == a
@show a == b
@show a == c
@show a == d
@show a != a
@show a != b
@show a != c
@show a != d
u = Dict{SK,Int}()
u[a] = 7
u[b] = 8
u[c] = 9
u[d] = 10
@show u[a]
@show u[b]
@show u[c]
@show u[d]
I don’t think it is wrong, but maybe the documentation could be made easier to understand?
Docstring of == mentions that it falls back to ===, which I believe is the case we will hit since even though the struct contains a collection the struct itself is not a collection.
Generic equality operator. Falls back to ===. Should be implemented for all types with a notion of
equality, based on the abstract value that an instance represents. For example, all numeric types are
compared by numeric value, ignoring type. Strings are compared as sequences of characters, ignoring
encoding. For collections, == is generally called recursively on all contents, though other
properties (like the shape for arrays) may also be taken into account.
Docstring of === mentions that immutable objects are compared by bit level contents (this is your struct) and immutable objects by pointers (this is the Vector inside your struct). So I think since the contents of you struct is mutable it will not be stored “inside” your struct, but your struct rather has a pointer to where on the heap those values are, resulting in the pointer being compared by default.
Determine whether x and y are identical, in the sense that no program could distinguish them. First
the types of x and y are compared. If those are identical, mutable objects are compared by address in
memory and immutable objects (such as numbers) are compared by contents at the bit level. This
function is sometimes called “egal”. It always returns a Bool value.