I’m trying to create a macro that will merge named tuples and change the key names by adding suffixes, in a way that’s type stable.
Suppose that it’s guaranteed that nt1
and nt2
are the same type of named tuples, i.e. they have the same key names and same value types. For example, nt1, and nt2 could be:
nt1 = (; a=1, b=2)
nt2 = (; a=3, b=4)
I would like a function mymerge(xx::Vararg[NT,N}) where {N,NT<:NamedTuple}
such that
mymerge(nt1, nt2) == (; a_1=1, b_1=2, a_2=3, b_2=4)
Suppose I provide a function that adds suffixes, say
_rename_suffix(x::Union{AbstractString,Symbol}, suffix) = Symbol(x, "_", suffix)
The naive way of doing this (by using merge
and building NamedTuples with suffixed keys inside a tuple comprehension) doesn’t work for me, because it’s not type stable.
So I’m trying to achieve the same behavior by using a @generated
function.
Here’s what I have so far:
@generated function mymerge(xx::Vararg{NT,N}) where{N,names_,types,NT<:NamedTuple{names_,types}}
TT = NamedTuple{
tuple(collect(Iterators.flatten(_rename_suffix.(names_, n) for n in 1:N))...),
Tuple{Iterators.flatten(fieldtypes(types) for _ in 1:N)...}
}
t = Iterators.flatten([[Expr(:call, :getfield, x, Meta.quot(name)) for name in names_] for x in xx]) # this line is wrong
Expr(:call, TT, t)
end
I believe that the type TT
is correct. However, t
is not. I get the error
nt1 = (; a=1, b=2)
nt2 = (; a=3, b=4)
mymerge(nt1, nt2)
MethodError: Cannot `convert` an object of type Expr to an object of type Int64
If this wasn’t a generated function, the way to do this could be
TT(tuple(Iterators.flatten((values(x) for x in xx)))...)
but of course that doesn’t work as @generated
functions can’t access the value of the arguments.