Can anyone explain to me what is going on in this chunk of code from Base?
@eval function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple}
if length(args) != length(names::Tuple)
throw(ArgumentError("Wrong number of arguments to named tuple constructor."))
end
# Note T(args) might not return something of type T; e.g.
# Tuple{Type{Float64}}((Float64,)) returns a Tuple{DataType}
$(Expr(:splatnew, :(NamedTuple{names,T}), :(T(args))))
end
It looks sort of like a generated function, but it’s not a generated function. Why is the @eval
needed for this constructor definition?
Here’s a permalink to the repo:
https://github.com/JuliaLang/julia/blob/89b018f56090a7371ce3826d8ea7d8aa92961a2e/base/namedtuple.jl#L84
It’s not a generated function and is not related. It’s just for emitting expression (splatnew) that doesn’t have a parsable syntax in this context.
1 Like
I see that this code was introduced in this PR to fix this bug. Before that PR, the constructor had a normal definition without @eval
.
Eval wasn’t the fix, the direct use of the new expression was. And the use of that expression necessitate the use of eval in this context as mentioned above.
Sure, I didn’t mean to imply that using @eval
is what fixed the problem.
To rephrase what you’re saying, there’s no normal syntax to create a Expr(:splatnew, ...)
expression, hence the usage of Expr(:splatnew, ...)
plus an eval
. As for why the Expr(:splatnew, ...)
is needed here in the first place, I’d have to do a bit more studying to figure that out…
There is syntax for it, inside the struct definition, but not in this context.
1 Like