Usage of `@eval` in Base code when defining a NamedTuple constructor

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