name
returns a Core.TypeName
. This is not the same as the type.
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.
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.
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.
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.
Check out Jameson’s last comment on that thread I linked for the best way to do this “non-sensical” operation according to him.
julia> struct Foo{T}
x::T
end
julia> Base.typename(Foo{Int64})
Foo
julia> VERSION
v"1.0.0"
What is wrong with handling the Core.TypeName
in your use case?
This returns Core.TypeName
, note a `Type.
I had defined methods that take a generic type parameter, such as MyType1
, that don’t recognize Core.TypeName
.
Ah, got it.
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.
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.
Looks good. But with this version is the generic type name known at compile-time?
Is it being available at compile time an issue? Since julia is JIT-compiled, is there much of a difference performance wise? I’d imagine if the compiler can’t prove either way if the type is going to change, it’s not going to replace that call with the concrete type at compile time anyway.
The function provided by @mohamed82008 is by definition runtime, since the call has to happen or it gets inlined if it’s not statically provable.
There is a difference in the sense that if the generic name gets known at compile time, the call to name(...)
gets optimized away. I don’t see why this information cannot be made available to the compiler.
You can mark that piece of code with @inline
to mark it as inlineable to the compiler, circumventing the (minimal) calling overhead.
What use case do you have for this? What do you want to do with that type?
The metaprogramming solution from @mohamed82008 can be generalized using subtypes
(in the InteractiveUtils
module, loaded when in REPL mode but needs importing otherwise):
for T in subtypes(AbstractMyType)
@eval f(x::$T) = $T
end
The downside is that if new subtypes are defined after this executes, they won’t be recognized.