-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Add syntactic sugar for covariant actual type parameters #6984
Comments
I agree something like this is necessary. This could also be addressed by triangular dispatch (#3766). |
The main motivation is to avoid having to explicitly parametrize methods in many cases (and also avoid having to create a lot of type aliases). Like
instead of
or
How would triangular dispatch help in this case? Or are you thinking about some other kind of case? |
Ah, I see your point. I was thinking of occasions where you have two or more nested types: immutable Foo{S <: AbstractArray}
data::S
end and then I want to dispatch on real arrays. At the moment, I don't think it's possible to dispatch directly. You could define typealias RArray{T<:Real} Array{T}
bar{S<:RArray}(a::Foo{S}) = ... Alternatively, if we had triangular dispatch bar{T<:Real,S<:Array{T}}(a::Foo{S}) = ... or, with your proposal bar(a::Foo{<:Array{<:Real}}) = ... |
Ok, that should be useful way to use this idea as well. |
+1 This makes a lot of function declarations more concise. |
+1 I would like to have this. I believe a bit of theoretical work is needed to figure out what |
Given the current semantics of covariant parametric types, I agree that this is really useful – but, as @JeffBezanson points out, not entirely clear yet. Basically, it allows you to write the covariant parametric type as |
|
@sunetos When parametric types are involved, things are more complicated. Making parametric types covariant by default may introduce other subtle problems. |
@sunetos, I find it frustrating when you speak on behalf of all "newcomers". Could you please stick to claims about your own beliefs rather than making assertions about the beliefs of all people? You don't represent me and I find it quite upsetting when you assert that you do. |
Please don't bow out. Your opinion is really valuable. I spoke up because I think it's important to keep in mind that none of us actually know how the modal newcomer will behave since we've never run any experiments. Right now, we're all just speaking from strong personal beliefs. |
@sunetos I am sympathetic to the proposal of having parametric types covariant by default, and believe that it may substantially improve the experience of many users. However, changing the default behavior from invariant to covariant is a major change to the language, which may potentially complicate type inference and method resolution. This needs extensive discussion. |
FWIW, I remember bumping into the " -Jacob |
I also found quite unintuitive the current |
I see the arguments in favor of covariance, but you do trade one surprise for another:
That code does not work for all arguments. Another "new surprise" is that |
@JeffBezanson's argument is spot on. When you want to input an array, you probably feel that covariance makes things convenient. However, when you want to supply a collection to receive output results, covariance is probably not what you want (you may like contravariance here) |
I also find I have a psychological bias regarding numeric arrays --- it is very tempting to think of a |
I didn't understand that example. In languages with concrete type inheritance, polymorphism, etc., you would specify the type that has something akin to the "least common denominator" of required behaviors. So if your function tried to do something that would only work on floats (like the example of assigning 1.5), then you would specify the argument as ::Vector{FloatingPoint}, not ::Vector{Real}, because clearly the function requires floats. If the function is generic enough to never require floats, but would work with any numeric type, then you would use ::Vector{Real} or ::Vector{Number}. |
Let's consider an example outside numerical computation: # an action can operate on any instance of type T
type Action{T} ... end
apply_action{T}(act::Action{T}, x::T) = ...
function apply_action(A::Vector{Float64}, act::Action{Float64})
for x in A
apply_action(act, x)
end
end In this case, we do wish to have contravariant behavior, which allows that we supply instances of It is true when we consider arrays, covariance might feel convenient. However, there are plenty of cases where the opposite behavior, namely contravariance, is preferred. C# is a language that puts a lot of thoughts in this issues, which allows to specify whether each type parameter is covariant, invariant, or contravariant. (see http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)) |
The point of the example is that writing to arrays asks for contravariance, in which case you need to explain to people why you have to write |
In spite of the debate about default behaviors, I think it is useful to provide syntactic sugar for people to express covariance when they need it. My feeling is that |
Ok, then I see what you mean. |
Yes, I think what @lindahua described would be a good way forward. |
Also extends to |
The bad news is that this is closer to being incompatible with the idea of default type parameters. But the good news is that #1470, adding a |
I got to this topic, as I need things like this nearly everywhere in my code. It might not be very constructive what I've to say, but this is one of the most important features in Julia, so I think it's extremely important that there are several iterations for the syntax to make all people feel comfortable with it. As much as I like JeffBezanson's proposal, I didn't grasp the syntax right away.
Some proposals I quickly came up with: # As a reference, the original proposal
::{T<:Real}->{A<:AbstractVector{T}} -> MyType{T,A}
# Leaving away curly braces and wrapping everything in brackets, to make it feel more like a type
::(T<:Real -> A<:AbstractVector{T} -> MyType{T,A})
# Introducing new parameters on the fly. (Maybe not easy to parse, or ambiguous?)
# Also you could define ambiguous parameters, with more than one type.
::MyType{T<:Real, A <: AbstractVector{T}}, ::SecondType{T, F <: FloatingPoint}
# That's already it (Maybe I can come up with more)
# To compare them better visually, here are all of them side by side:
::{T<:Real} -> {A <: AbstractVector{T}} -> MyType{T,A}
::(T<:Real -> A <: AbstractVector{T} -> MyType{T,A})
::MyType{T <: Real, A <: AbstractVector{T}} The first thing that I notice is, that putting MyType in the first place is a lot easier on the eye, as you immediately know the type everything is about, which helps to put things into context.... |
I hear you on the excessive amount of punctuation. However one problem we |
Still wanted. Part of the type revamp issue: #8974 |
+1 for just supporting We don't need a sugar that covers all possible cases, since in more complicated cases you can always fall back onto the current syntax |
Should this be added to the 1.0 milestone? |
With the new |
Someone might have already mentioned this but I think an issue for this syntax is that it is unclear where the |
It seems pretty clear to me that the You can always use |
Just pushed a PR. This is pretty simple to implement given the new |
As per discussion in https://groups.google.com/forum/#!topic/julia-users/alavN8tRdyI :
Let e.g.
act like
RArray
given byThis convenient shortcut should go a long way in those cases where people would want covariant types.
The type parameter of
Array{<:Real}
might become anonymous or hidden; it probably doesn't matter so much since you would rarely re-provide it, e.g.Array{<:Real}{Int}
.The text was updated successfully, but these errors were encountered: