Regarding this part of the question specifically, note that Julia specializes code for the given argument types when compiling the function.
Indeed if this is the case then it seems to extend the generalization of subtypes to collections of subtypes would seem intuitive, and apparently possible in other languages - but I see now there is a different philosophy/justification for type invariance.
But it is very much possible in julia too, by typing e.g. Vector{<:Real}
It’s just, that the default is invariant und you have to request other behaviours explicitly.
If you declare an argument type to be Vector{Real}, you are asserting that the argument must be a vector that can hold Float64andInt64andRationaland any other subtype of Real. Here’s an example of a function that actually requires the argument to be of type Vector{Real} in order to work properly:
function foo(v::Vector{Real})
for x in v
if x isa Int
println("Found an Int.")
elseif x isa Float64
println("Found a Float64.")
else
println("Found a different subtype of Real.")
end
end
end
julia> x = Real[1, 2.3, 3//4]
3-element Vector{Real}:
1
2.3
3//4
julia> foo(x)
Found an Int.
Found a Float64.
Found a different subtype of Real.
The difference is not in the initial content of the vector, but in what can be put into it by the function. Suppose the function accepts Vector{Real}, does push!(v, 2.5) and you passed Vector{Int} – the function will fail unexpectedly. That’s why it is prohibited.
Note, yyou don’t need to define subtypes (or any types) in function definitions (but you want to for structs, for speed), e.g. not:
g(x::Array{Real}) = x .* 2
You can just do e.g.:
g(x) = x .* 2
and it’s NEVER slower, nor “wrong”. But it allows more than you might think, and that’s ok (and for something that doesn’t make sense, means a runtime error):
julia> pow2(x) = x ^ 2
julia> pow2("Palli")
"PalliPalli"
If you want to be specific you want some type of array, document that way for your users, then it’s neither wrong, but then you may want to be as general as possible, i.e. not just allow Aarry, but any subtype of AbstractArray, and likely containing any type (or maybe of just some).
Since noone seems to have linked it so far, there’s also this explanation by @StefanKarpinski (though with a slight Rust bend, as that was the original question there):
Indeed the heterogenous array case I also failed to think about, but would be a case where subtypes should not implicitly allowed. Thanks - was informative.