# Method specificity with parametric type constructors

**URL:** https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385
**Category:** Internals & Design
**Tags:** parametric-types, dispatch
**Created:** [January 28, 2022, 5:10pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385 "2022-01-28T17:10:21Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![danielmatz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielmatz/32/2285_2.png) [@danielmatz](https://discourse.julialang.org/u/danielmatz)
#### Post date: [January 28, 2022, 5:10pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/1 "2022-01-28T17:10:21Z")

</div>

I’ve encountered what appears to be an inconsistency with method specificity. However, since it’s considerably more likely that I’m just confused, I thought I’d ask here before creating an issue.

My basic expectation is that in the following simple example the method that uses a type parameter constrains the two arguments to be of the same type, and so it is more specific:

```julia
julia> foo(x::Integer, y::Integer) = "generic"
julia> foo(x::T, y::T) where {T <: Integer} = "specific"

julia> foo(1, 2)
"specific"

julia> foo(Int8(1), 2)
"generic"

```

That indeed works as expected. If I introduce a parametric type:

```julia
julia> struct Foo{T <: Integer}
    x::T
    y::T
end

julia> methods(Foo)
# 1 method for type constructor:
[1] Foo(x::T, y::T) where T<:Integer in Main

julia> Foo(x::Integer, y::Integer) = "generic"

julia> Foo(1, 2)
Foo{Int64}(1, 2)

julia> Foo(Int8(1), 2)
"generic"

```

That also follows my expectations. If I introduce one more parametric type:

```julia
julia> struct Bar{T}
    x::Foo{T}
    y::Foo{T}
end

julia> methods(Bar)
# 1 method for type constructor:
[1] Bar(x::Foo{T}, y::Foo{T}) where T in Main

julia> Bar(x::Foo, y::Foo) = "generic"

julia> Bar(Foo(1, 2), Foo(1, 2))
"generic"

julia> Bar(Foo(Int8(1), Int8(2)), Foo(1, 2))
"generic"

```

Why didn’t the first call to `Bar` dispatch to the constructor provided by Julia?

---

<div class="post-metadata">

### Author: ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)
#### Post date: [January 28, 2022, 5:58pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/2 "2022-01-28T17:58:32Z")

</div>

Good question. I believe there is some nuance (maybe even a bug) when dealing with parametric type constructors, because I have already seen confusing problems related to them popping up in the Discourse before.

My takeaway from these previous problems is that parameterized struct constructors may behave kinda weirdly if you do not parameterize the call with the desired type. So, in your case:

```julia
julia> Bar{Int}(Foo(1, 2), Foo(1, 2))
Bar{Int64}(Foo{Int64}(1, 2), Foo{Int64}(1, 2))

```

Gives the expected result, however, you cannot parameterize your outer constructors so:

```julia
julia> Bar{Int}(Foo(Int8(1), Int8(2)), Foo(1, 2))
ERROR: MethodError: Cannot `convert` an object of type 
  Foo{Int8} to an object of type 
  Foo{Int64}
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:171
  Foo{Int64}(::Any, ::Any) where T<:Integer at REPL[5]:2
Stacktrace:
 [1] Bar{Int64}(::Foo{Int8}, ::Foo{Int64}) at ./REPL[10]:2
 [2] top-level scope at REPL[17]:1

```

I believe that maybe `methods` is lying to us, and there is in fact a `Bar(x::Any, y::Any)` which calls the adequate parameterized version.

---

<div class="post-metadata">

### Author: ![danielmatz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielmatz/32/2285_2.png) [@danielmatz](https://discourse.julialang.org/u/danielmatz)
#### Post date: [January 28, 2022, 6:58pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/3 "2022-01-28T18:58:48Z")

</div>

> [@Henrique\_Becker](#):
>
> I believe that maybe `methods` is lying to us, and there is in fact a `Bar(x::Any, y::Any)` which calls the adequate parameterized version.

I’m not sure why you think `methods` is lying. I think you are just seeing the distinction between `methods(Bar)` and `methods(Bar{Int})`:

```julia
julia> methods(Bar)
# 1 method for type constructor:
[1] Bar(x::Foo{T}, y::Foo{T}) where T in Main

julia> methods(Bar{Int})
# 1 method for type constructor:
[1] Bar{T}(x, y) where T in Main

```

> [@Henrique\_Becker](#):
>
> My takeaway from these previous problems is that parameterized struct constructors may behave kinda weirdly if you do not parameterize the call with the desired type.

In my case, I am trying to write this generically, so I don’t know the parametric type in order to call `Bar{T}(x, y)` directly. My first instinct is to write a method to grab the right parametric type. Something like:

```julia
Bar(x::Foo{T}, y::Foo{T}) where {T} = Bar{T}(x, y)

```

But, of course, that’s the method that Julia already supplied that is causing problems.

I was able to work around this issue by being careful to not define the `Bar(::Foo, ::Foo)` method, so that the Julia-provided constructor is still accessible. Something like:

```julia
# Don't define this method from above
# Bar(x::Foo, y::Foo) = "generic"

Foo(x::Integer) = Foo(x, x)
Foo(x::Foo) = x
Base.convert(::Type{Foo{T}}, x::Foo) where {T} = Foo{T}(x.x, x.y)
Base.promote_rule(::Type{Foo{S}}, ::Type{Foo{T}}) where {S, T} = Foo{promote_type(S, T)}

Bar(x, y) = Bar(promote(Foo(x), Foo(y))...)

```

---

<div class="post-metadata">

### Author: ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)
#### Post date: [January 28, 2022, 9:25pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/4 "2022-01-28T21:25:47Z")

</div>

That does seem like a bug doesn’t it.

What happens with this instead?

```julia
Bar(x::Foo{S}, y::Foo{T}) where {S,T} = "generic"

```

---

<div class="post-metadata">

### Author: ![danielmatz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielmatz/32/2285_2.png) [@danielmatz](https://discourse.julialang.org/u/danielmatz)
#### Post date: [January 28, 2022, 9:30pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/5 "2022-01-28T21:30:49Z")

</div>

> [@cjdoris](#):
>
> What happens with this instead?
> 
> ```julia
> Bar(x::Foo{S}, y::Foo{T}) where {S,T} = "generic"
> 
> ```

Oh, very clever! That does work!

---

<div class="post-metadata">

### Author: ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)
#### Post date: [January 28, 2022, 9:35pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/6 "2022-01-28T21:35:33Z")

</div>

At least something works! But I would have thought those two versions would be equivalent dispatch-wise (there are no cases where one matches and the other doesn’t) so feels like a bug to me.

---

<div class="post-metadata">

### Author: ![danielmatz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielmatz/32/2285_2.png) [@danielmatz](https://discourse.julialang.org/u/danielmatz)
#### Post date: [January 28, 2022, 9:36pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/7 "2022-01-28T21:36:32Z")

</div>

> [@cjdoris](#):
>
> so feels like a bug to me.

I’ll make an issue.

---

<div class="post-metadata">

### Author: ![danielmatz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielmatz/32/2285_2.png) [@danielmatz](https://discourse.julialang.org/u/danielmatz)
#### Post date: [January 28, 2022, 9:49pm UTC](https://discourse.julialang.org/t/method-specificity-with-parametric-type-constructors/75385/8 "2022-01-28T21:49:23Z")

</div>

[https://github.com/JuliaLang/julia/issues/43977](https://github.com/JuliaLang/julia/issues/43977)
