# Passing through \`==\` for subtypes of a parametric abstract type with differing parameters

**URL:** https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716
**Category:** General Usage
**Created:** [April 15, 2026, 12:25am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716 "2026-04-15T00:25:51Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![brainandforce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brainandforce/32/211054_2.png) [@brainandforce](https://discourse.julialang.org/u/brainandforce)
#### Post date: [April 15, 2026, 12:25am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/1 "2026-04-15T00:25:51Z")

</div>

Say I define `AbstractFoo{T}`, which has no supertype besides `Any`, but has subtypes `Bar{T}` and `Baz{T}`:

```julia-repl
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-repl
julia> qux(x::AbstractFoo) = x.value

```

By default, `==` falls back to `===`, so `Bar{S}(value)` will not equal `Bar{T}(value)` in general:

```julia-repl
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-repl
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

```julia-auto
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

```julia-auto
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?

---

<div class="post-metadata">

### Author: ![lilachint](https://avatars.discourse-cdn.com/v4/letter/l/ee7513/32.png) [@lilachint](https://discourse.julialang.org/u/lilachint)
#### Post date: [April 15, 2026, 12:45am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/2 "2026-04-15T00:45:55Z")

</div>

This could work.

```julia
Base.:(==)(x::AbstractFoo, y::AbstractFoo) = typejoin(typeof(x), typeof(y)) |> isstructtype && qux(x) == qux(y)

```

Since `struct` could only subtype an abstract type, this checks if the closest common ancestor type of `x` and `y` is an abstract type. If it is an abstract type, they do not subtype a common struct, and thus it must be false; otherwise, test the two with `qux`.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 15, 2026, 2:14am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/3 "2026-04-15T02:14:24Z")

</div>

> [@brainandforce](#):
>
> So a more general method is needed. You can’t define
> 
> ```julia-auto
> Base.:(==)(x::AbstractFoo, y::AbstractFoo) = qux(x) == qux(y)
> 
> ```
> 
> if you don’t want all `AbstractFoo` subtypes to be considered `==` equal

Am I misreading this? It sounds like you’re asking for a general `AbstractFoo` equality method at first, but you don’t want this exact method because there should be exceptions, that is some pairs of subtypes can’t be equal. It just seems to be a really unusual characteristic, and the exceptions are too vague to suggest anything specific.

---

<div class="post-metadata">

### Author: ![lilachint](https://avatars.discourse-cdn.com/v4/letter/l/ee7513/32.png) [@lilachint](https://discourse.julialang.org/u/lilachint)
#### Post date: [April 15, 2026, 2:27am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/4 "2026-04-15T02:27:07Z")

</div>

In my understanding, as in my first reply, I think they meant the comparison should only work for the same subtype of `AbstractFoo`, thus `Bar` and `Bar` but not `Bar` and `Baz`, also not for arbitrary `AbstractFoo` subtypes. It’s a weird thing to consider but I guess it might be needed somehow.

---

<div class="post-metadata">

### Author: ![brainandforce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brainandforce/32/211054_2.png) [@brainandforce](https://discourse.julialang.org/u/brainandforce)
#### Post date: [April 15, 2026, 3:51am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/5 "2026-04-15T03:51:57Z")

</div>

> [@lilachint](#):
>
> This could work.
> 
> ```julia-auto
> Base.:(==)(x::AbstractFoo, y::AbstractFoo) = typejoin(typeof(x), typeof(y)) |> !isabstracttype && qux(x) == qux(y)
> 
> ```

Perhaps it would be better to use `isstructtype` rather than `!isabstracttype`, but the distinction may not matter for this particular case?

> [@Benny](#):
>
> It just seems to be a really unusual characteristic, and the exceptions are too vague to suggest anything specific.

Basically, if `qux(x) == qux(y)`, then `x == y` for all structs descending from `AbstractFoo`, if they share the same struct type.

Maybe to make it more concrete, perhaps you have a supertype `AbstractScaledShape{T}`, derived types `RightTriangle{T}`, `Square{T}`, etc, and getter `scale_factor(::AbstractScaledShape)`. The shapes will be different and it wouldn’t make sense for different shapes to be equal in any way, but `Square{S}(x) == Square{T}(x)` and `RightTriangle{S}(x) == RightTriangle{T}(x)` for any `x` convertible to `S` and `T` would make sense.

I guess this may be a problem where dispatch tools aren’t actually the right approach.

---

<div class="post-metadata">

### Author: ![lilachint](https://avatars.discourse-cdn.com/v4/letter/l/ee7513/32.png) [@lilachint](https://discourse.julialang.org/u/lilachint)
#### Post date: [April 15, 2026, 4:04am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/6 "2026-04-15T04:04:35Z")

</div>

That’s a really solid example. I modified my reply to use `isstructtype`, it should work for this scenario? Could you elaborate on why you’d think dispatching is not the ‘right’ approach?

---

<div class="post-metadata">

### Author: ![brainandforce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brainandforce/32/211054_2.png) [@brainandforce](https://discourse.julialang.org/u/brainandforce)
#### Post date: [April 15, 2026, 4:10am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/7 "2026-04-15T04:10:15Z")

</div>

> [@lilachint](#):
>
> Why would dispatching not be the ‘right’ approach?

Just in the sense that you can’t accomplish the desired type selection with Julia’s tools for method dispatch; as in your solution the tests have to be done in the function body.

Perhaps there is a way, but I can’t conceptualize how the method signature would look.

---

<div class="post-metadata">

### Author: ![lilachint](https://avatars.discourse-cdn.com/v4/letter/l/ee7513/32.png) [@lilachint](https://discourse.julialang.org/u/lilachint)
#### Post date: [April 15, 2026, 4:11am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/8 "2026-04-15T04:11:59Z")

</div>

Indeed, that method is definitely not using argument type dispatch per se, which is unfortunate. `UnionAll` could only narrow down to concrete types, I’m not optimistic enough to think it could be changed, since there might be scenarios stopping this from happening.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 15, 2026, 4:45am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/9 "2026-04-15T04:45:31Z")

</div>

> [@brainandforce](#):
>
> Basically, if `qux(x) == qux(y)`, then `x == y` for all structs descending from `AbstractFoo`, if they share the same struct type.

Weren’t you close with this?

```julia-auto
Base.:(==)(x::AbstractFoo, y::AbstractFoo) = false
Base.:(==)(x::T, y::T) where {T<:AbstractFoo} = qux(x) == qux(y)

```

---

<div class="post-metadata">

### Author: ![lilachint](https://avatars.discourse-cdn.com/v4/letter/l/ee7513/32.png) [@lilachint](https://discourse.julialang.org/u/lilachint)
#### Post date: [April 15, 2026, 4:47am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/10 "2026-04-15T04:47:53Z")

</div>

This iterated dispatch would make the comparison of `Bar(0)` and `Bar(0.0)` to return `false`, for instance, which is not the desired outcome.

---

<div class="post-metadata">

### Author: ![brainandforce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brainandforce/32/211054_2.png) [@brainandforce](https://discourse.julialang.org/u/brainandforce)
#### Post date: [April 15, 2026, 4:48am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/11 "2026-04-15T04:48:50Z")

</div>

No, because the second method would only dispatch if `x` and `y` are _exactly_ the same type. If `typeof(x) === Bar{Int16}` and `typeof(y) === Bar{BigFloat}`, it would dispatch to the first method and always return `false`.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 15, 2026, 5:59am UTC](https://discourse.julialang.org/t/passing-through-for-subtypes-of-a-parametric-abstract-type-with-differing-parameters/136716/12 "2026-04-15T05:59:39Z")

</div>

Ah I get it now, I was stuck thinking of concrete types, missing the parametric struct types (abstract but not `abstract type`). I think you’re right that method signatures don’t support this; method parameters only stand in for full types `::T` or type parameters `::Vector{T}`, not parametric types `::T{Int}`; I don’t know type theory but that’s probably for reasonable attempts to sort sets of methods by specificity.

You could technically split an algorithm across a helper multimethod dispatched over Holy traits, singletons mapped to ideally statically computed conditions, to get around limitations of method dispatch. But a `Samestructtype()` trait method is not really warranted here because lilachint got it done in one method, even one line.
