Extract type name only from parametric type

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.

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

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)


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.

1 Like
julia> struct Foo{T}

julia> Base.typename(Foo{Int64})

julia> VERSION

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.

1 Like

Do you mean this?

help?> Core.TypeName
  No documentation found.


  mutable struct Core.TypeName <: Any


  name        :: Symbol
  module      :: Module
  names       :: Core.SimpleVector
  wrapper     :: Type
  cache       :: Core.SimpleVector
  linearcache :: Core.SimpleVector
  hash        :: Int64
  mt          :: Any

julia> Base.typename(Vector{Int})

julia> typeof(ans)

julia> Base.typename(Vector{Int}).wrapper

julia> typeof(ans) 

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?

1 Like

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

The downside is that if new subtypes are defined after this executes, they won’t be recognized.

1 Like