Consider the following code:
julia> nt = (x=1, y=2)
(x = 1, y = 2)
julia> k1 = keys(nt)[1]
:x
julia> :($k1)
:x
julia> :($(keys(nt)[1]))
:x
julia> nt.:($k1)
1
julia> nt.:($(keys(nt)[1]))
ERROR: syntax: invalid syntax "nt.(keys(nt)[1])"
It seems to me that this behavior is inconsistent. My question is:
- Is using of
$
to interpolate into :()
a feature or a hack (in which case it should not be recommended to be used).
- If it is a feature is it possible to make the case of a single identifier and an expression to be handled consistently?
Thank you!
1 Like
The problem here is that :()
doesn’t quite work that way and :($(keys(nt)[1]))
is equivalent to keys(nt)[1]
after parsing. For more discussion on this behavior see also this thread:
https://discourse.julialang.org/t/any-rational-behind-symbol-true-true/27597
The solution here would be to just call getproperty
directly.
1 Like
For 1, yes, this is an explicit and frequently-used feature. Typical use of :(…)
is exactly identical to quote … end
. And splicing things into expressions like this is a key part of Julia’s metaprogramming — it is documented in that section of the manual.
For 2, this is an interaction between .
-property access and :
quoting. I think this may be a bug, but this is complicated because .
is so overloaded. For example, we’re just a stone’s throw away from a broadcast:
julia> nt = (x=1,y=2)
(x = 1, y = 2)
julia> x = :y
:y
julia> nt.:(x)
1
julia> nt.:($x)
2
julia> nt.:($(x,))
ERROR: MethodError: objects of type NamedTuple{(:x, :y),Tuple{Int64,Int64}} are not callable
Stacktrace:
[1] _broadcast_getindex_evalf at ./broadcast.jl:625 [inlined]
[2] _broadcast_getindex at ./broadcast.jl:598 [inlined]
[3] getindex at ./broadcast.jl:558 [inlined]
[4] copy at ./broadcast.jl:824 [inlined]
[5] materialize(::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0},Nothing,NamedTuple{(:x, :y),Tuple{Int64,Int64}},Tuple{Base.RefValue{Symbol}}}) at ./broadcast.jl:814
[6] top-level scope at REPL[22]:1
julia> sqrt.:($(rand(3),))
3-element Array{Float64,1}:
0.8930844290914318
0.3289781971083193
0.9250148515294918
1 Like
Thank you both for the comments. I was not aware that :()
was equivalent to quote
.
However, it seems that the interpolation does not happen at parse time:
julia> Meta.parse(raw":($(keys(nt)[1]))")
:($(Expr(:quote, :($(Expr(:$, :((keys(nt))[1])))))))
julia> Meta.parse(raw"quote $(keys(nt)[1]) end")
:($(Expr(:quote, quote
#= none:1 =#
$(Expr(:$, :((keys(nt))[1])))
end)))
So the substitution must happen at some later time.
Just to add, the following parses as expected:
julia> eval(Meta.parse(raw":(nt.$(keys(nt)[1]))"))
:(nt.x)
julia> eval(eval(Meta.parse(raw":(nt.$(keys(nt)[1]))")))
1