This is an interesting question! I’ll point out some suspected confusion: when metaprogramming in Julia, code is not represented with strings, but rather expression trees. If you interpolate a string into @eval
, you’ll get a literal string:
julia> e = "2 + 2"
"2 + 2"
julia> :( a = $e ) # this is what `@eval a = $e` would execute
:(a = "2 + 2")
julia> e = :(2 + 2) # instead, deal with expressions
:(2 + 2)
julia> :( a = $e )
:(a = 2 + 2)
You’d never represent code as a string unless it comes to you that way — in which case you can Meta.parse
it into an expression.
On to the question: you have a named tuple which you want to use as default keyword arguments for some method definitions:
const PARAMETERS = (; a = 1, b = 2)
function query(; a = 1, b = 2) # you don’t want to repeat yourself here
a + b
end
One way to do this is like so:
const PARAMETERS = (; a = 1, b = 2)
kws = [Expr(:kw, k, v) for (k, v) in pairs(PARAMETERS)]
@eval function query(; $(kws...))
a + b
end
You can see the unevaluated code by replacing @eval
with quote … end
. This requires a little knowledge of how keyword arguments are parsed internally, so I’ll explain how I got there.
If you inspect the structure of an expression with keyword arguments, you’ll see they’re actually Expr(:kw, k, v)
objects.
julia> :( f(; a = 1) ) |> dump
Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol f
2: Expr
head: Symbol parameters
args: Array{Any}((1,))
1: Expr
head: Symbol kw
args: Array{Any}((2,))
1: Symbol a
2: Int64 1
julia> :( f(; a = 1, b = 2) ).args[2].args # these are the `a = 1, b = 2`
2-element Vector{Any}:
:($(Expr(:kw, :a, 1)))
:($(Expr(:kw, :b, 2)))
To properly interpolate (; a = 1, b = 2)
into an expression as keyword arguments, we have to convert them into a collection of Expr(:kw, k, v)
structs. That is what the kws
line above does. Finally, the dot syntax in f(; $(kws...))
is equivalent to f(; $(kws[1]), $(kws[2]), …, $(kws[end]))
. Hope this helps.