I don’t think this is quite correct. If it were the case, you could not have
x::Any
Here, x
is effectively a reference to some data along with some metadata which provides runtime information about what it is. (The exact compiler output might differ a bit from this, but those pieces of information must be there.)
So why not
x::AbstractString
It could be a String
or some other kind of string thing which is a subtype of AbstractString
.
This seems like a more accurate statement. This seems like an arbitrary rule of the type system/core language.
There isn’t an equivalent for Real
, for example. If you were to return a Real
from a function, I am fairly confident that doesn’t convert to Float64
.
I’ve checked and the behavior for this is the same:
julia> function floatFunction()::Real
x::Float64=1.0
return x
end
floatFunction (generic function with 1 method)
julia> typeof(floatFunction())
Float64
julia> function vectorFunction()::DenseVector
v::Vector{Float64} = [1.0]
return v
end
vectorFunction (generic function with 1 method)
julia> typeof(vectorFunction())
Vector{Float64} (alias for Array{Float64, 1})
julia> function vectorFunction2()::DenseVector
v::Vector{Real} = [1.0]
return v
end
vectorFunction2 (generic function with 1 method)
julia> typeof(vectorFunction2())
Vector{Real} (alias for Array{Real, 1})
julia> isconcretetype(DenseVector)
false
It seems like only the first “level” of a type. The outer type? Whatever this is called - only that bit is optimized to a concrete type. I guess that makes sense, the compiler is just doing regular type inference and it finds a way to put a tighter constraint on your return type.
I should point out, it seems type annotations on return types of functions appear to behave differently to type annotations on variables.
… actually, this seems to be an important point. I guess when you realize this, the behavior observed above makes a lot more sense.
This, I think, is the point being made also by @danielwe above?