Generally, instances don’t contain or reference the expression was evaluated to make them, that’s why we use reflection to track down method bodies. Also, interpolation inserts those instances or references to them into an expression, it doesn’t inverse-evaluate them into subexpressions. Inverse is probably the wrong word in the mathematical sense because evaluation is not bijective (many expressions can evaluate to the same thing, so which should be picked).
julia> :(a, $(Ref{Int})) |> dump
Expr
head: Symbol tuple
args: Array{Any}((2,))
1: Symbol a
2: Ref{Int64} <: Any
julia> :(a, Ref{Int}) |> dump
Expr
head: Symbol tuple
args: Array{Any}((2,))
1: Symbol a
2: Expr
head: Symbol curly
args: Array{Any}((2,))
1: Symbol Ref
2: Symbol Int
On the other hand, it doesn’t look like you need reflection or an inverse evaluation because you’re not after the full definition struct Foo{T} end and its methods. Your intended result is pretty simple that we could just piece it together. Bear in mind this digs into internal details that can change across minor revisions, so it’s worth checking them, like fieldnames(typeof(bar)).
julia> unsetparameters(T::Type) = Base.typename(T).wrapper
unsetparameters (generic function with 1 method)
julia> struct Foo{T} end
julia> unsetparameters(Foo)
Foo
julia> unsetparameters(Foo{Float64}) # not sure if works elsewhere
Foo
julia> function expressunion(T::UnionAll)
params = Symbol[]
U = T
while U isa UnionAll
push!(params, Symbol(U.var))
U = U{U.var} # isa DataType with enough TypeVars
end
:( $(Symbol(T)){ $(params...) } )
end
expressunion (generic function with 1 method)
julia> expressunion(Foo)
:(Foo{T})
julia> expressunion(unsetparameters(Foo{Float64}))
:(Foo{T})
No idea what you’re going to do with that but good luck.
Thanks! For context, what I was trying to do was to define a struct from a collection of other concrete types, such that the fields of the new type was the disjoint union of the fields of the input types:
struct Foo{T}
foo1::Vector{T}
foo2::Matrix{T}
end
struct Bar{T}
bar1::Vector{T}
bar2::T
end
@concat_struct Bax{T} begin
Foo{T}
Bar{T}
end
# Evaluates to:
struct Bax{T}
foo1::Vector{T}
foo2::Matrix{T}
bar1::Vector{T}
bar2::T
end
Based on the difficulties you’ve described, I eventually went with a simple composite type, though that required me doing some weird nonsense to access fields-of-components in a type-stable way. (I just messed around with stuff until constant propagation worked correctly…)
struct Bax{T}
foo::Foo{T}
bar::Bar{T}
end
function Base.getproperty(bax::Bax, s::Symbol)
if hasfield(Foo, s)
return getfield(getfield(bax, :foo), s)
elseif hasfield(Bar, s)
return getfield(getfield(bax, :bar), s)
else
return getfield(bax, s)
end
end
Base.@pure hasfield(T, s) = s in fieldnames(T)