What is difference between Type{T} and T

Hello everyone,
I have just started to learn about Julia(<:1.0).

Unfortunately, I am not able to understand the use/functionality of Type{T}, for example, in

convert(::Type{T}, a::T) where {T<:AbstractArray} = a

why cannot we use

convert(::T, a::T) where {T<:AbstractArray} = a

please excuse me if this question is too basic.

4 Likes

did you read the documentation?

https://docs.julialang.org/en/v1/manual/types/#man-singleton-types-1

also, for an example,

https://docs.julialang.org/en/v1/manual/methods/#Extracting-the-type-parameter-from-a-super-type-1

The documentation of convert is

convert(T, x)

  Convert x to a value of type T.

And an example is

julia> convert(Int, 3.0)
  3

So you can see the first argument is a Type, so its type is Type{T}, and the second argument is an object to be converted to that type.

3 Likes

foo(::T, a::S) where {T,S} - the first argument of foo is a (non-usable) variable of type T, the second, namely a of type S.

foo(::Type{T}, a::S)- the first argument of foo is the type T, the second is of type S.

So, the difference between ::T and ::Type{T} is that they request either a variable of type T or a variable which is the type T itself. In julia types are data as well.

Indeed, this is a very basic question that is easily addressed by reading the documentation and actually running the code in question and seeing its effects.

10 Likes

Please everyone, keep in mind that this question was appropriately posted in the usage / first steps category. Even if something is documented, it’s still not a problem to ask a question about it! Hopefully the answers here help clarify things for @vickysharma0812.

51 Likes

Thank you for reply,
I have understood the usage and importance of Type{T}.

regards
Vikas

Type{T} is admittedly slightly unintuitive as it provides an unexpected branch in the type tree

DataType <-------- Float64
Type{Float64} <-/
julia> typeof(Float64)
DataType

julia> Float64 isa DataType
true

julia> Float64 isa Type{Float64} # This in comibnation with the next line might be unexpected
true

julia> DataType isa Type{Float64} 
false

which the documentation hints at by calling it β€œspecial”

3 Likes

Just to add on, often, when you are new to something, even if you read the documentation not everything clicks. More often than not you are unsure why some passage or sentence matters until you’ve understood the concepts. This is why it’s helpful to point to the documentation and explain briefly the concepts as well.

15 Likes

A quick question: is DataType the root of the β€œtype tree”?

And one longer one: how/where is this tree defined?

Think of DataType as the representation of types. I am not sure would consider the latter a tree in any sense; perhaps clarify what you mean.

No, Any is the root of the type tree. Putting aside that I see DataType as kind of a β€œmeta type”:

julia> supertype(DataType)
Type{T}

julia> supertype(Type{T} where T)
Any

julia> supertype(Any)
Any

or with a small helper function:

julia> show_supertypes(T) = print(join(supertypes(T), " <: "))
show_supertypes (generic function with 1 method)

julia> show_supertypes(DataType)
DataType <: Type{T} <: Any

On a side note, if you want to see a branch of the type tree you can do

using AbstractTrees
AbstractTrees.children(x) = subtypes(x)
print_tree(Number)

to get

julia> print_tree(Number)
Number
β”œβ”€ Complex
└─ Real
   β”œβ”€ AbstractFloat
   β”‚  β”œβ”€ BigFloat
   β”‚  β”œβ”€ Float16
   β”‚  β”œβ”€ Float32
   β”‚  └─ Float64
   β”œβ”€ AbstractIrrational
   β”‚  └─ Irrational
   β”œβ”€ Integer
   β”‚  β”œβ”€ Bool
   β”‚  β”œβ”€ Signed
   β”‚  β”‚  β”œβ”€ BigInt
   β”‚  β”‚  β”œβ”€ Int128
   β”‚  β”‚  β”œβ”€ Int16
   β”‚  β”‚  β”œβ”€ Int32
   β”‚  β”‚  β”œβ”€ Int64
   β”‚  β”‚  └─ Int8
   β”‚  └─ Unsigned
   β”‚     β”œβ”€ UInt128
   β”‚     β”œβ”€ UInt16
   β”‚     β”œβ”€ UInt32
   β”‚     β”œβ”€ UInt64
   β”‚     └─ UInt8
   └─ Rational
9 Likes

@carstenbauer, I guess you know this, but according to print_tree, DataType is a leaf on the β€œtree”. (It is the unique element on the branch of subtypes.)

However, according to isa (mentioned in @baggepinnen’s post), all the following evaluate to true:

  • isa(Type, Any)
  • isa(Any, Type)
  • isa(DataType, Any)
  • isa(Any, DataType).

But DataType is not really at the top/bottom of the order because, in addition, we have

  • isa(Type, DataType) is false
  • isa(DataType, Type) is true.

But also note that this violation of transitivity (DataType <: Type ~: Any ~: DataType) implies that, once we include Singleton Types, we do not have a preorder let alone a partial order or a tree.

I might try and create a graph on the basis of isa to get a lovely diagram like yours as a way of honing my Julia skills. At the moment, I can’t see a completely obvious way on the basis of the existing functions in AbstractTrees.

PS. Just out of interest, we also have

  • isa(typeof(Type), DataType) is true
  • isa(DataType, typeof(Type)) is false
    and
  • isa(typeof(Type{T} where T), DataType) is true
  • isa(DataType, typeof(Type{T} where T)) is false.
1 Like

PPS. How come your types are all blue and mine are not? :slight_smile:

Hm? What do you mean?

He probably wrote:

```julia
code here
```

For some minimal highlight support.

1 Like

I think you are confusing things. isa shows if the type of the first argument <: the second argument. So does not make sense to me to use isa to establish any ordering/tree, you should be using <: for it.

julia> Any <: Type
false

julia> Type <: Any
true

julia> Any <: DataType
false

julia> DataType <: Any
true

You should not use isa passing a type as the first argument, because it will always be the same as DataType <: second_isa_argument because the type of any type will be DataType.

1 Like

That’s not true:

julia> Int64 isa Type{Int64}
true

julia> DataType <: Type{Int64}
false

I can’t find it, but there is a recording of a talk, probably given by Jeff, that shows a 2D grid layout of values, with one dimension showing a subtyping relation and another dimension showing the typeof relation. It shows the following self-loops:

julia> typeof(DataType)
DataType

julia> supertype(Any)
Any

If someone can find that talk, I would appreciate a time-link to it and a screenshot of that slide.

3 Likes

Let’s agree that because

julia> isa(Any, DataType)
true

and

julia> isa(Any, Any)
true

we can safely say that Any is both of type Any and of type DataType. The whole point of type hierarchies is to be able to think of β€œmeta types” as being inside the system. Otherwise we are artificially restricting ourselves to reasoning/operating at a lower level.

Let’s also agree that isa and <: can be described as two distinct binary relations, both of which describe relationships among types. You will note that on Types Β· The Julia Language, there are certain more abstract types where isa is used instead of <: . I expect that <: does not account for relationships higher-up the type hierarchy.

I guess the first natural question to ask is whether <: is a partial order.

Also, although my arguments in the previous post show that isa is not a preorder. They do not exclude the possibility that <: is a subrelation of isa, where some symmetry relationships under isa are broken by <: .

You are correct, I forgot this exception. However, Type{T} where {T} is kinda of a special object, it represents the singleton type for which the only instance is T (this is literally the documentation of Core.Type). I believe we cannot have typeof(Int64) as Type{Int64} because then typeof(typeof(Int64)) would be Type{Type{Int64}} and so on, instead of the cycle at DataType but I am not sure what would break by doing this.

If you tried to follow what I meant by my statement then you missed it:

julia> isa(Any, DataType)
true

julia> typeof(Any) <: DataType
true

As far as I understand it, that is correct, but there’s certainly reason to doubt myself and be confused about it:

2 Likes