To me they seem to be the same. Since Type
is of type DataType
and vice-versa, how are they not equivalent? When should I use one versus the other?
> isa(DataType, Type)
true
> typeof(Type)
DataType
> isa(Type, DataType)
true
> typeof(DataType)
DataType
Types in Julia are basically the data elements of various different sizes and functionality. Every value(not variable) in a program is bound to be of some type.
We can use the function typeof(x) to find the data type of the variable x.
A return type can be specified in the function declaration using the :: operator. This converts the return value to the specified type. This function will always return an Int8 regardless of the types of x and y .
Abstract types allow the construction of a hierarchy of types, providing a context into which concrete types can fit. This allows you, for example, to easily program to any type that is an integer, without restricting an algorithm to a specific type of integer.
Type
and DataType
are themselves both types. And the type of most types is, as you've found, DataType
. In this case, DataType
is a subtype of the abstract Type
:
julia> supertype(DataType)
Type{T}
julia> supertype(Type)
Any
julia> DataType <: Type
true
This means that anything that isa
DataType
will also be a Type
— so many types in Julia are isa
both. There are also other subtypes of Type
, including Union
s and TypeConstructor
s. This means that all types in julia will be of type Type
, but even simple things like Vector
won't be of type DataType
.
Type
is special. As you see above, it's parametric. This allows you to precisely specify the type of a specific type in question. So while every single type in Julia isa
Type
, only Int
isa
Type{Int}
:
julia> isa(Int, Type{Int})
true
julia> isa(Float64, Type{Int})
false
julia> isa(Float64, Type)
true
This ability is special and unique to Type
, and it's essential in allowing dispatch to be specified on a specific type. For example, many functions allow you to specify a type as their first argument.
f(x::Type{String}) = "string method, got $x"
f(x::Type{Number}) = "number method, got $x"
julia> f(String)
"string method, got String"
julia> f(Number)
"number method, got Number"
It's worth noting that Type{Number}
is only the type of Number
, and not the type of Int
, even though Int <: Number
! This is parametric invariance. To allow all subtypes of a particular abstract type, you can use a function parameter:
julia> f(Int)
ERROR: MethodError: no method matching f(::Type{Int64})
julia> f{T<:Integer}(::Type{T}) = "integer method, got $T"
f (generic function with 3 methods)
julia> f(Int)
"integer method, got Int64"
The ability to capture the specific type in question as a function parameter is powerful and frequently used. Note that I didn't even need to specify an argument name — the only thing that matters in this case is the parameter within Type{}
.
So that was a pretty long explanation for what's really a pretty short answer: You typically don't want to use DataType
, since it won't cover all types in Julia. Instead you should use Type
to describe the type of any or all types. Use Type{T}
when you want to describe the type of T
in particular.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With