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 Unions and TypeConstructors. 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