Broader (non-concrete) types in NamedTuple

I wonder if constructing NamedTuple{names, T}(::S) where S <: T is a bug, eg

julia> VERSION

julia> NamedTuple{(:a, :b)}((missing, 10))
(a = missing, b = 10)

julia> NamedTuple{(:a, :b),Tuple{Union{Missing, Float32},Union{Missing, Int16}}}((missing, 10))
NamedTuple{(:a, :b),Tuple{Union{Missing, Float32},Union{Missing, Int16}}}((missing, 10))

Why would it be a bug? Both the NamedTuples you constructed are valid NamedTuples.


julia> Tuple{Union{Missing, Float32},Union{Missing, Int16}}((missing, 10))
(missing, 10)

julia> typeof(ans)

so you cannot do this with Tuple.

If you think of a NamedTuple as a container implemented using a Tuple, then this is OK, but if you think of it as “a Tuple with names” then this is inconsistent.


Related: NamedTuples are apparently not covariant:

julia> NamedTuple{(:a,), Tuple{Int}} <: NamedTuple{(:a,), Tuple{Integer}}

julia> Tuple{Int} <: Tuple{Integer}
1 Like

Looking at the source, it seems that having broader types for the fields is something that is allowed, at least takes special care when printing.

@jeff.bezanson, since you wrote most of this, can you please clarify the intention?

also @cstjean’s point about lack of covariance and whether this is intended.

It’s not clear whether NamedTuples (or, indeed, tuples! see should be covariant, but for now they are intentionally invariant. The main motivation was of course missing data, where allowing Union{T,Missing} as the field type of a concrete type is potentially useful.