I’m going to attempt to rescue this thread before it gets completely buried. I might not be able to answer all your questions.
2
Parametric types with their parameters unspecified are abstract types. In particular, they are referred to as UnionAll Types. They represent the union of all types that are possible by assigning a concrete type to each type parameter.
Array
is shorthand for Array{T,N} where N where T
. Note that you can partially instantiate a parametric type:
julia> Array{Int64} == Array{Int64,N} where N
true
A partially instantiated parametric type is still an abstract type:
julia> isconcretetype(Array)
false
julia> isconcretetype(Array{Float64})
false
julia> isconcretetype(Array{Float64,1})
true
3
Yes, if we restrict ourselves to one-dimensional arrays, then we have the following concrete type:
Array{Union{Int,String},1}
If the number of dimensions is left unspecified, then we have an abstract type:
julia> IntStringArray{N} = Array{Union{Int, String}, N} where N
Array{Union{Int64, String},N} where N
julia> isconcretetype(IntStringArray)
false
julia> isconcretetype(IntStringArray{2})
true
julia> x = IntStringArray(undef, 0)
0-element Array{Union{Int64, String},1}
julia> push!(x, 4)
1-element Array{Union{Int64, String},1}:
4
julia> push!(x, "hello")
2-element Array{Union{Int64, String},1}:
4
"hello"
julia> push!(x, 1.2)
ERROR: MethodError: Cannot `convert` an object of type Float64 to an object of type Union{Int64, String}
1
I’m not sure if this is a point of confusion or not, but a type such as Type{Int64}
is a sort of meta-type that represents the type object Int64
rather than an instance of Int64
. For example, we can make two separate foo
methods as follows:
julia> foo(::Int64) = 1
foo (generic function with 1 method)
julia> foo(::Type{Int64}) = 2
foo (generic function with 2 methods)
julia> methods(foo)
# 2 methods for generic function "foo":
[1] foo(::Int64) in Main at REPL[1]:1
[2] foo(::Type{Int64}) in Main at REPL[2]:1
julia> foo(100)
1
julia> foo(Int64)
2
Regarding the difference between
Type{T} where T<:Union{Int, String}
and
Union{Type{Int}, Type{String}}
…They do seem to be functionally equivalent, but Julia doesn’t seem to be able to do the algebra/set operations needed to declare them ==
. I’m not sure precisely what’s going on there.
Edit:
It could be because the first is a UnionAll
type and the second is a Union
type:
julia> typeof(Type{T} where T<:Union{Int, String})
UnionAll
julia> typeof(Union{Type{Int}, Type{String}})
Union
It looks like Julia doesn’t have a specialized ==
method for comparing Union
and UnionAll
types:
julia> (Type{T} where T<:Union{Int, String}) == Union{Type{Int}, Type{String}}
false
julia> @which (Type{T} where T<:Union{Int, String}) == Union{Type{Int}, Type{String}}
==(T::Type, S::Type) in Base at operators.jl:162
It might be possible to implement a specialized ==(::Union, ::UnionAll)
method that would catch this sort of scenario.