Any reason to prefer `::Type{T}` over `T::Type`?

Suppose you have a function which takes a type as argument, say for example convert function. If you want to constrain this type, idiomatic Julia seems to be the following:

convert(::Type{T}, x::Foo) where {T <: Integer}

However, as far as I understand, one could just as well write this as:

convert(T::Type{<:Integer}, x::Foo)

which at least to my eyes reads much cleaner. However, I’ve never seen this in the wild.

Is there a compelling reason to prefer the idiomatic (top) version?

1 Like

See Performance Tips · The Julia Language

4 Likes

The answer says

function f_type(t)  # or t::Type

will not specialize, but

function g_type(t::Type{T}) where T

will.

convert(T::Type{<:Integer}, x::Foo)

is not exactly the same as either of those, but looks more like the second one than the first to me.

I think the main difference is just that you have access to T in the function body, asked about a similar case a whole ago.

1 Like

Strictly speaking, these are more equivalent argument names:

convert(S::Type{T}, x::Foo) where {T<:Integer} = ... # body can use S or T
convert(S::Type{T} where {T<:Integer}, x::Foo) = ... # body can use S

Only the first makes the method share that parameter. One use is making multiple type annotations share that parameter, like foo(::Type{T}, t::T) where T. As mentioned before, It also helps force specialization with respect to that argument in the exceptional cases that Julia doesn’t do it for you, specifically when the argument has a type Type, Function, or Vararg.