# Construct Tuple containing NamedTuple from Tuple

**URL:** <https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954>\
**Category:** General Usage\
**Tags:** tuple, namedtuple\
**Created:** [May 16, 2023, 7:41pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954 "2023-05-16T19:41:43Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![baumgold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baumgold/32/28576_2.png) [@baumgold](https://discourse.julialang.org/u/baumgold)\
**Post date:** [May 16, 2023, 7:41pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/1 "2023-05-16T19:41:43Z")

</div>

I’m trying to construct a Tuple containing a member NamedTuple from a Tuple. I believe this should work but it doesn’t.

```julia
julia> T = Tuple{Int64, Int64, NamedTuple{(:d, :e), Tuple{Int64, Int64}}};

julia> vals = (1,2,(3,4));

julia> T(vals)
ERROR: MethodError: Cannot `convert` an object of type Tuple{Int64, Int64} to an object of type NamedTuple{(:d, :e), Tuple{Int64, Int64}}

Closest candidates are:
  convert(::Type{NamedTuple{names, T}}, ::NamedTuple{names, T}) where {names, T<:Tuple}
   @ Base namedtuple.jl:148
  convert(::Type{NamedTuple{names, T}}, ::NamedTuple{names}) where {names, T<:Tuple}
   @ Base namedtuple.jl:151
  convert(::Type{T}, ::T) where T
   @ Base Base.jl:64
  ...

Stacktrace:
 [1] cvt1
   @ ./essentials.jl:418 [inlined]
 [2] ntuple
   @ ./ntuple.jl:50 [inlined]
 [3] convert(#unused#::Type{Tuple{Int64, Int64, NamedTuple{(:d, :e), Tuple{Int64, Int64}}}}, x::Tuple{Int64, Int64, Tuple{Int64, Int64}})
   @ Base ./essentials.jl:419
 [4] Tuple{Int64, Int64, NamedTuple{(:d, :e), Tuple{Int64, Int64}}}(x::Tuple{Int64, Int64, Tuple{Int64, Int64}})
   @ Base ./tuple.jl:364
 [5] top-level scope
   @ REPL[5]:1

```

There seem to be two solutions that I can see:

1. Modify the [Tuple constructor](https://github.com/JuliaLang/julia/blob/v1.9.0/base/tuple.jl#L364) to construct each element explicitly:

```julia
(::Type{T})(x::Tuple) where {T<:Tuple} = tuple((type(arg) for (type, arg) in zip(T.parameters, x))...)

```

1. Implement `Base.convert` for NamedTuples:

```julia
Base.convert(::Type{NT}, val::Tuple) where {NT<:NamedTuple} = NT(val)

```

Am I missing something obvious? Will either of these proposed solutions cause problems elsewhere?

Thanks for the feedback!

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [May 16, 2023, 8:12pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/2 "2023-05-16T20:12:03Z")

</div>

basically, you’re saying the following two should either both work or both not work:

```julia
julia> T = NamedTuple{(:d, :e), Tuple{Int64, Int64}}
NamedTuple{(:d, :e), Tuple{Int64, Int64}}

julia> T((1,2))
(d = 1, e = 2)

julia> convert(T, (1,2))
ERROR: MethodError: Cannot `convert` an object of type Tuple{Int64, Int64} to an object of type NamedTuple{(:d, :e), Tuple{Int64, Int64}}

```

---

<div class="post-metadata">

**Author:** ![baumgold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baumgold/32/28576_2.png) [@baumgold](https://discourse.julialang.org/u/baumgold)\
**Post date:** [May 16, 2023, 9:24pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/3 "2023-05-16T21:24:38Z")

</div>

Yes I believe that’s a nice succinct way to put it. Any thoughts on which direction is correct?

---

<div class="post-metadata">

**Author:** ![RomainPct](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/romainpct/32/49995_2.png) [@RomainPct](https://discourse.julialang.org/u/RomainPct)\
**Post date:** [May 17, 2023, 10:01am UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/4 "2023-05-17T10:01:53Z")

</div>

Hey Ben,

what about:

```julia
@inline ntconvert(::Type, x) = x
@generated function ntconvert(::Type{T}, x) where {T<:NamedTuple}
    fn = fieldnames(T)
    ft = fieldtypes(T)
    e = Expr(:tuple)
    for (i, (n, t)) in enumerate(zip(fn, ft))
        push!(e.args, Expr(:(=), n, :(ntconvert($t, x[$i]))))
    end
    e
end

```

```julia
julia> const T = NamedTuple{(:a,:b), Tuple{Int,Int}}
         const T2 = NamedTuple{(:a,:b,:c), Tuple{Int,Int,T}}
         b = (1,2,(3,4))
         @benchmark ntconvert(T2, $b)
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 2.915 ns … 45.083 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 3.887 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 3.890 ns ± 1.068 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▇▂ █▄ ▁▂▂▃▃▃▂▁ ▁▁▁ ▂
  ██▃▁▁▁▁▁▁▁▃▁▁▁▁▁▁▃▁▁▃▃▁██▁▄▄▅▅▅▄▇▇▇████████████████████▇█▇ █
  2.92 ns Histogram: log(frequency) by time 5.31 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

It does not seem to be a noop though. I don’t exactly know why. I’m not sure.

---

<div class="post-metadata">

**Author:** ![baumgold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baumgold/32/28576_2.png) [@baumgold](https://discourse.julialang.org/u/baumgold)\
**Post date:** [May 18, 2023, 3:20pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/5 "2023-05-18T15:20:26Z")

</div>

I made the following PR that should solve my issue. Feedback appreciated.

> <https://github.com/JuliaLang/julia/pull/49879>
>
> See the discussion from Discourse:
> 
> https://discourse.julialang.org/t/construc…t-tuple-containing-namedtuple-from-tuple

---

<div class="post-metadata">

**Author:** ![RomainPct](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/romainpct/32/49995_2.png) [@RomainPct](https://discourse.julialang.org/u/RomainPct)\
**Post date:** [May 18, 2023, 3:49pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/6 "2023-05-18T15:49:26Z")

</div>

From the manual:  
“since `convert` can be called implicitly, its methods are restricted to cases that are considered “safe” or “unsurprising””. So it’s not completely surprising that `MyNamedTupleType(mytuple)` works but no `convert(MyNamedTupleType, mytuple)`.

Maybe the reason this conversion is not implemented is because it assumes an ordering on the fields of the tuple? Curious to see the review of the PR!

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 19, 2023, 8:00am UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/7 "2023-05-19T08:00:56Z")

</div>

I wouldn’t really expect the code from the first post to work, neither for tuples nor for any other type. In general, it corresponds to running

```julia
struct SomeType
    a::Int
    b::AnotherType
end

SomeType(1, (2, 3))

```

and expecting that to be turned into `SomeType(1, AnotherType(2, 3))`. Doesn’t make much sense, IMO.

---

<div class="post-metadata">

**Author:** ![baumgold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baumgold/32/28576_2.png) [@baumgold](https://discourse.julialang.org/u/baumgold)\
**Post date:** [May 19, 2023, 11:11am UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/8 "2023-05-19T11:11:18Z")

</div>

@aplavin - Interestingly, construction between Tuple/NamedTuple already works for simple types:

```julia
julia> @NamedTuple{a::Int}((1,))
(a = 1,)

julia> Tuple{Int}((a=1,))
(1,)

```

The problem arises when one of the members needs to be implicitly converted, like the example above.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 19, 2023, 2:46pm UTC](https://discourse.julialang.org/t/construct-tuple-containing-namedtuple-from-tuple/98954/9 "2023-05-19T14:46:52Z")

</div>

I think these examples are completely unrelated.

> [@baumgold](#):
>
> ```julia
> julia> Tuple{Int}((a=1,))
> (1,)
> 
> ```

Is creating a named tuple from the elements you give it: the `1` is taken as-is, not transformed in any way.

Same, similar to your first post, you can construct a tuple with a tuple:

```julia
julia> T = Tuple{Int64, Int64, Tuple{Int64, Int64}};
julia> vals = (a=1,b=2,c=(3,4));
julia> T(vals)
(1, 2, (3, 4))

```

Again, here provided values are taken as-is.
