# Understanding how subtypes work within NamedTuple

**URL:** <https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442>\
**Category:** New to Julia\
**Created:** [August 27, 2022, 8:45pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442 "2022-08-27T20:45:22Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![adow031](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adow031/32/16403_2.png) [@adow031](https://discourse.julialang.org/u/adow031)\
**Post date:** [August 27, 2022, 8:45pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/1 "2022-08-27T20:45:22Z")

</div>

I’ve been using the `Real` type in some of my functions that take direct user-input, to avoid spurious errors around the use of `Int` or `Float64`; however, I encountered an issue when trying to do this with an argument to a function that was `NamedTuple{(:a, :b), Tuple{Real, Real}}`.

The function below works as expected, and accepts `Tuple` arguments with any combination of `Float64` and `Int`.

```julia
function testA(x::Tuple{Real,Real})
    print(x)
end

```

However, the function `testB` returns error for every combination of `Int` and `Float64` within the `NamedTuple`.

```julia
function testB(x::NamedTuple{(:a, :b),Tuple{Real,Real}}) 
    print(x)
end

```

I have a workaround:

```julia
function testC(x::NamedTuple{(:a, :b), Tuple{T, U}} where {T <: Real , U <: Real})
    print(x)
end

```

This works as expected for all inputs of `Int` and `Float64`.

To summarise my question: why don’t `Tuple` and `NamedTuple` work the same way?

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [August 27, 2022, 8:57pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/2 "2022-08-27T20:57:31Z")

</div>

What you see here is maybe similar to the behaviour explained here  
[https://m3g.github.io/JuliaNotes.jl/stable/typevariance/](https://m3g.github.io/JuliaNotes.jl/stable/typevariance/)

I actually also have to read up why `Tuple{Float64} <: Tuple{Real}` works 😃 but as the post shows, `NamedTuple` and also `Vector` behave similarly here.

---

<div class="post-metadata">

**Author:** ![adow031](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adow031/32/16403_2.png) [@adow031](https://discourse.julialang.org/u/adow031)\
**Post date:** [August 27, 2022, 9:09pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/3 "2022-08-27T21:09:31Z")

</div>

Thanks.

I can see why this would be required with vectors, since the function could try to alter them and an error would occur if the type weren’t correct.

However, since Tuples are immutable, I think that allowing subtypes to be substituted makes sense.

---

<div class="post-metadata">

**Author:** ![Jollywatt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jollywatt/32/202198_2.png) [@Jollywatt](https://discourse.julialang.org/u/Jollywatt)\
**Post date:** [August 27, 2022, 9:28pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/4 "2022-08-27T21:28:57Z")

</div>

> [@adow031](#):
>
> ```julia
> function testC(x::NamedTuple{(:a, :b), Tuple{T, U}} where {T <: Real , U <: Real})
> print(x)
> end
> 
> ```

By the way, your workaround may be written as:

```julia
function testC(x::NamedTuple{(:a, :b), Tuple{<:Real,<:Real}})
    print(x)
end

```

or even with `<:Tuple{Real,Real}` instead.

* * *

(Meta-comment: Perhaps you don’t even need to bother declaring your method with such specific types. There really is no harm in just defining `testC(x)` or `testC(x::NamedTuple)` unless you are needing specific behaviour from multiple dispatch.)

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [August 27, 2022, 9:31pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/5 "2022-08-27T21:31:00Z")

</div>

This is just how the Julia type system is designed. Tuple types are contravariant, and they’re, in fact, the only non-invariant concrete types (hope I’m not misusing terminology):

```julia
julia> NamedTuple{(:a, :b),Tuple{Int, Int}} <: NamedTuple{(:a, :b),Tuple{Real, Real}}
false

julia> Tuple{Int} <: Tuple{Real}
true

```

Relevant FAQ entry (especially the second paragraph):  
[Frequently Asked Questions · The Julia Language](https://docs.julialang.org/en/v1/manual/faq/#Why-doesn't-it-work-to-declare-foo(bar::Vector%7BReal%7D)-42-and-then-call-foo(%5B1%5D))?

For more, see the Julia Manual, and perhaps even Wikipedia.

BTW, another, more general, approach for your “workaround” would be something like this:

```julia
julia> testG(x::NamedTuple{syms, <:NTuple{n, Real}}) where {syms, n} = print(x)
testG (generic function with 1 method)

julia> testG((a = 1, c = 3))
(a = 1, c = 3)
julia> testG((z = 4.3, r = big"5"))
(z = 4.3, r = 5)

```

This should work for any `NamedTuple`, as long as all values are subtypes of `Real`.

---

<div class="post-metadata">

**Author:** ![adow031](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adow031/32/16403_2.png) [@adow031](https://discourse.julialang.org/u/adow031)\
**Post date:** [August 27, 2022, 9:38pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/6 "2022-08-27T21:38:50Z")

</div>

Thanks for the suggestions. I should be able to tidy my code up using the in-line `<:`.

(I’m using explicit types so that errors can be caught early.)

---

<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:** [August 27, 2022, 9:42pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/7 "2022-08-27T21:42:22Z")

</div>

Hopefully the following is a bit easier to digest than the various “\_\_-variance.”

The _general_ rule is that an abstract type _parameter_ only serves as a restriction of field/element types (often implemented as pointers to boxes) and does not make the type itself abstract. So a `Vector{Real}` is a concrete type you can instantiate; it isn’t a supertype (which are abstract) of `Vector{Int}` or `Vector{Float64}`. `Vector{T} where T<:Real`, or as an older shorthand `Vector{<:Real}`, is an abstract supertype of all the aforementioned types.

Tuple is the notable exception to that rule. Tuples are a special type in the language to package many _concrete_ instances into one instance. There’s no way for a concrete Tuple to specify a particular element as being restricted by an abstract type, the way the concrete `Vector{Real}` restricts elements. Confusingly, abstract type parameters are allowed for Tuple, but since it can’t mean an element type restriction, it instead _does_ make the type abstract. You can consider `Tuple{Real}` to be equivalent to `Tuple{T} where T<:Real`.

NamedTuple contains a Tuple (which is why the Tuple’s type is a type parameter of NamedTuple), but unlike the Tuple, it still follows the general rule.

> [@nsajko](#):
>
> Tuple types are contravariant, and they’re, in fact, the only non-invariant concrete types (hope I’m not misusing terminology)

I’m not really clear on the terminology myself, but Vararg is also weird like Tuple e.g `Vararg{Int,3} <: Vararg{Real,3}`. Vararg are not concrete types, but they do sometimes annotate varargs methods so it’s worth looking out for.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [August 27, 2022, 10:09pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/8 "2022-08-27T22:09:53Z")

</div>

> [@Benny](#):
>
> but Vararg is also weird like Tuple

I think this may be because Vararg is syntax sugar for NTuple, or something like that. They’re related for sure.

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [August 31, 2022, 12:06pm UTC](https://discourse.julialang.org/t/understanding-how-subtypes-work-within-namedtuple/86442/9 "2022-08-31T12:06:45Z")

</div>

The Julia developer documentation has a good explanation relevant to this discussion for the `Tuple` part of the `NamedTuple` type here:  
[https://docs.julialang.org/en/v1/devdocs/types/#Tuple-types](https://docs.julialang.org/en/v1/devdocs/types/#Tuple-types)
