Good observation! It actually can be really useful to put data up in the type-domain. The simplest example is probably multidimensional arrays; in Julia, they are represented by the AbstractArray{T, N}
type, where N
is an integer representing the number of dimensions, i.e. Matrix{Float64} === Array{Float64, 2}
. Note that it’s not the type Integer
, but the literal number 2.
As a more complicated example, I wrote a blog post about encoding a permutation as part of a type to permute vectors very quickly (at the cost of recompiling the function whenever you change the permutation).
Having values as part of types is mentioned in the docs too,
- Both abstract and concrete types can be parameterized by other types. They can also be parameterized by symbols, by values of any type for which
isbits
returns true (essentially, things like numbers and bools that are stored like C types or struct
s with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted.
To answer your question,
May have instances at all?
I think the answer is no. But that doesn’t mean they can’t be used, e.g.
julia> struct MyType{T} end
julia> f(::Type{MyType{T}}) where T = T[1] + 1
f (generic function with 1 method)
julia> X = MyType{(5,6,7)}
MyType{(5, 6, 7)}
julia> f(X)
6
By the way, the result here is actually computed at compile-time, e.g. in
julia> @code_warntype f(X)
Variables
#self#::Core.Const(f)
#unused#::Core.Const(MyType{(5, 6, 7)})
Body::Int64
1 ─ %1 = Base.getindex($(Expr(:static_parameter, 1)), 1)::Core.Const(5)
│ %2 = (%1 + 1)::Core.Const(6)
└── return %2
we can see the Core.Const(6)
result is known to the compiler to be a constant.
Lastly, just to comment on
Should be disallowed?
Generally Julia tries to not impose restrictions on users unless they are needed for performance or correctness, so I think even if they weren’t so fundamental as to be used in Array
, they should not be disallowed.