Say I define AbstractFoo{T}, which has no supertype besides Any, but has subtypes Bar{T} and Baz{T}:
julia> abstract type AbstractFoo{T}
end
julia> struct Bar{T} <: AbstractFoo{T}
value::T
end
julia> struct Baz{T} <: AbstractFoo{T}
value::T
end
Since others might want to add subtypes of AbstractFoo, we have a getter method for its value parameter as part of the interface, so even if a subtype doesn’t define a field named value, you define qux for it.
julia> qux(x::AbstractFoo) = x.value
By default, == falls back to ===, so Bar{S}(value) will not equal Bar{T}(value) in general:
julia> Bar{Float32}(1) == Bar{Int}(1)
false
julia> Baz{BigInt}(1) == Baz{ComplexF64}(1)
false
To get around this, you could just define equality methods for Bar and Baz instances:
julia> Base.:(==)(x::Bar, y::Bar) = qux(x) == qux(y)
julia> Bar{Float32}(1) == Bar{Int}(1)
true
julia> Base.:(==)(x::Baz, y::Baz) = qux(x) == qux(y)
julia> Baz{BigInt}(1) == Baz{ComplexF64}(1)
true
But this doesn’t solve the problem for anyone who derives a subtype of AbstractFoo. So a more general method is needed. You can’t define
Base.:(==)(x::AbstractFoo, y::AbstractFoo) = qux(x) == qux(y)
if you don’t want all AbstractFoo subtypes to be considered == equal, and there’s no point in defining
Base.:(==)(x::T, y::T) where {T<:AbstractFoo} = qux(x) == qux(y)
because that will only dispatch if both x and y are exactly the same type (which == already handles).
What is the correct way to write an equality method for == in this case, where I want to dispatch on types sharing a struct definition but differing in parameter?