Just to add to the above: that section of the docs is intentionally demonstrating that you can interpolate an Expr into another Expr. This is different from simply interpolating some object in that place. Consider an expression which does not evaluate to itself (the Tuple does, as @ffevotte demonstrated above), i.e. not a “literal”.
julia> :(a in $:(1+1))
:(a in 1 + 1)
julia> :(a in 1+1)
:(a in 1 + 1)
julia> :(a in $(1+1))
:(a in 2)
In the first case, the expression is interpolated as-is. The second case is exactly the same as first. Interpolating the 1+1
in the first expression produces the second expression, the same as if I had written it out by hand. In the last case, the value of 1+1
is computed, then the interpolation takes place.
Why does this distinction matter? Consider:
julia> :(a in $:((x + 1)))
:(a in x + 1)
julia> :(a in $(x + 1))
ERROR: UndefVarError: x not defined
Notice that x
doesn’t exist, so the second case throws an error. It’s impossible to compute x+1
at the moment. However, I can create an expression that when evaluated computes x+1
as part of it (that’s what the first case does). By the time it’s evaluated, I can ensure that some variable called x
exists in scope so the whole thing works. This kind of composing of larger expressions out of smaller expressions is extremely important, particularly when writing macros. You might see building up of expressions like the following:
julia> var = :x;
julia> e = :($var + 1);
julia> f = :(sin ∘ Float16);
julia> small_ex = :($f.(1:$e))
:((sin ∘ Float16).(1:x + 1))
julia> larger_ex = :(map($var -> $small_ex, 1:5))
:(map((x->begin
(sin ∘ Float16).(1:x + 1)
end), 1:5))
julia> eval(ans)
5-element Vector{Vector{Float16}}:
[0.8413, 0.909]
[0.8413, 0.909, 0.1411]
[0.8413, 0.909, 0.1411, -0.757]
[0.8413, 0.909, 0.1411, -0.757, -0.959]
[0.8413, 0.909, 0.1411, -0.757, -0.959, -0.2793]
In each step above, I’m interpolating an expression into place, not the value of that expression. The eval
is just to demonstrate that that expression is valid, and what it does. In practice, macros end up “eval-ing” themselves after the expression is created.