e3c6
August 28, 2018, 12:55pm
1
Suppose I have a function that takes a value of a parametric type, for example:
abstract type AbstractMyType{Q} end
struct MyType1{Q} <: AbstractMyType{Q} end
struct MyType2{Q} <: AbstractMyType{Q} end
f(x::T) where {T<:AbstractMyType} = # something...
Inside f
, I want to determine the type of x
in a generic way, but without the parameters. For example, if f
si called with x::MyType1{Int}
, I want to have a variable inside f
that holds the value MyType1
.
For example one way to do it is to define methods:
f(x::T) where {T<:MyType1} = begin t = MyType1; #=more code... =# end
f(x::T) where {T<:MyType2} = begin t = MyType2; #=more code... =# end
But this gets repetitive if I have many derived types. Is there a better way?
1 Like
One way using metaprogramming:
for T in (MyType1, MyType2)
@eval getname(::Type{<:$T}) = $T
end
f(x::T) = begin t = getname(T); #=more code... =# end
2 Likes
e3c6
August 28, 2018, 1:43pm
3
This assumes you know the list of derived types (MyType1, MyType2)
. Hopefully there is a generic way?
This is probably bad practice but here is the relevant code from that post:
name(T::DataType) = T.name
name(T::UnionAll) = name(T.body)
1 Like
e3c6
August 28, 2018, 1:50pm
6
name
returns a Core.TypeName
. This is not the same as the type.
1 Like
Ya sorry missed that, name(::Type{T}) where T = eval(nameof(T))
also works but it is not as performant as my first solution, so you can call it a tradeoff.
e3c6
August 28, 2018, 2:01pm
8
Thanks that works. But the computation of T
here is not occurring at compile time? Is this what you mean when you say this is not as performant?
eval
is usually frowned upon in a function cuz ya it could be anything in there and it is evaluated in global scope so
f() = eval(:(a = 1))
actually defines a
outside of f
, so it is a bit tricky to reason about when inside a function and is a sign you are doing things in a non-Julian way.
1 Like
e3c6
August 28, 2018, 2:17pm
10
The type is not computed at compile-time.
thename(::Type{T}) where {T} = eval(nameof(T))
f(x::T) where {T} = thename(T)
@code_warntype f(2)
produces:
Body::Any
1 ─ %1 = (Base.getfield)(Int64, :name)::Core.TypeName
│ %2 = (Base.getfield)(%1, :name)::Symbol
│ %3 = invoke Main.eval(%2::Symbol)::Any
└── return %3
(I highlighted the Any).
This is a big turnoff.
mauro3
August 28, 2018, 2:26pm
11
Have you read the linked thread?
Why not:
mytype(::MyType1) = MyType1
mytype(::MyType2) = MyType2
# etc
f(x::AbstractMyType) = begin t = mytype(x); #=more code... =# end
Granted, it is one additional line per subtype, but not more.
2 Likes
Check out Jameson’s last comment on that thread I linked for the best way to do this “non-sensical” operation according to him.
1 Like
julia> struct Foo{T}
x::T
end
julia> Base.typename(Foo{Int64})
Foo
julia> VERSION
v"1.0.0"
2 Likes
What is wrong with handling the Core.TypeName
in your use case?
e3c6
August 29, 2018, 11:58am
16
This returns Core.TypeName
, note a `Type.
e3c6
August 29, 2018, 11:59am
17
I had defined methods that take a generic type parameter, such as MyType1
, that don’t recognize Core.TypeName
.
Just in case I was not clear, I meant to use .wrapper
and adapt the code from Jameson’s comment as follows:
name(::Type{T}) where {T} = (isempty(T.parameters) ? T : T.name.wrapper)
Sorry should have been more clear.
3 Likes
Sukera
August 29, 2018, 12:27pm
20
Do you mean this?
help?> Core.TypeName
No documentation found.
Summary
≡≡≡≡≡≡≡≡≡
mutable struct Core.TypeName <: Any
Fields
≡≡≡≡≡≡≡≡
name :: Symbol
module :: Module
names :: Core.SimpleVector
wrapper :: Type
cache :: Core.SimpleVector
linearcache :: Core.SimpleVector
hash :: Int64
mt :: Any
julia> Base.typename(Vector{Int})
Array
julia> typeof(ans)
Core.TypeName
julia> Base.typename(Vector{Int}).wrapper
Array
julia> typeof(ans)
UnionAll
Core.TypeName.wrapper
seems to be what you want.
3 Likes