Thank you for illuminating and detailed response. How about the proposal:
- add
P{=:T}syntax to mean the current meaning ofP{T} - make
P{T}a sugar toP{<:T}
This way we have nice consistent syntax P{<:T}, P{=:T}, P{>:T} to talk about covariance, invariance and contravariance of T respectively.
With this we can make P{T} to be a sugar for one of those 3 options. Which one? I argue that it must be P{<:T}. The immediate objection here is that it is not much harder to write P{<:T} with the current syntax and that is a fair point. However, imagine a typical MATLAB/Python/R user coming to Julia. It is fair to assume that he is not familiar with the notion of subtype variance. Once he learns about parametric types and type hierarchies he naturally conceives of the notion of subtype covariance (since it is an amalgamation of those two ideas) and tries to write something like f(v::Vector{Real}) while what he intended was f(v::Vector{<:Real}). And here we wreck the train for him by explicitly teaching that Vector{Real} contrary to his expectations is invariant (indeed there is even warning about it in the documentation). In doing so we introduce notion of subtype variance making the first Julia experience more complicated than is necessary.
This is exactly what happened to me on my first encounter with Julia. As a result of this confusion I asked the this question and you can see @dpsanders trying to explain type invariance to me there.
In summary, giving P{T} the meaning of P{<:T} has the following advantages:
- follows principle of least surprise
- makes the Julia’s learning curve less steep
- when one writes
P{T}with abstractT, he almost never meansP{=:T}and meansP{<:T}instead - compiler can generate efficient machine code for
f(v::Vector{<:Real}), while the currentlyf(v::Vector{Real})results in inefficient code.