Possible issue with `unique` when overloading `isequal`

I am trying to understand why calling unique in the following example returns a vector containing two elements which are equal as per isequal:

struct Tag
    tag::Int
end

Base.:(==)(t1::Tag,t2::Tag) = abs(t1.tag) == abs(t2.tag)  

t1 = Tag(1); t2 = Tag(-1);
isequal(t1,t2) # true
v = unique([t1,t2]) # returns [t1,t2]

So it seems that v contains both t1 and t2, but isequal(t1,t2) is true, which seems puzzling to me given the documentation of unique. Am I missing something?

If you look at the docstring if isequal you read:

This typically means that types for which a custom == or isequal method exists must
implement a corresponding hash method (and vice versa).

similarly in the docstring of == you can read:

isequal falls back to ==, so new methods of == will be used by the Dict type to compare keys. If your type will be used as a dictionary key, it should therefore also implement hash.

The conclusion is that you should have implemented hash method for your Tag, which can be done e.g. like this (with this implementation hash of Tag will be the same as hash of abs of tag field stored in it):

Base.hash(x::Tag, h::UInt) = hash(abs(x.tag), h)

And now you have:

julia> unique([t1,t2])
1-element Array{Tag,1}:
 Tag(1)
5 Likes

Thanks!