Open lrhn opened 3 years ago
Type parameters on the class name of a constructor must be valid type arguments to the class itself, and they are implicitly applied. Inside the constructor, the type parameters of the class are not in scope, they're replaced by the type parameters of the constructor. (Or, rather, all constructors have type parameters, if you don't write them, they are copied from the class, and constructors always only see their own type arguments.)
How would this play with #647, allowing constructors to have type arguments of their own?
It would be unrelated to #647. Those constructor-specific extra type arguments go after the name, not after the class, and are not constrained by the class generics.
You would be able to write C<T extends num>.name<X extends T>(X value) : _foo = value;
.
In this particular case (where the constructor is named) it would be possible to use a static method rather than a constructor, yielding very similar results:
class TreeSet<E> {
TreeSet.from(List<E> elements, int Function(E, E) compare);
static TreeSet<E> fromComparable<E extends Comparable<E>>(List<E> elements) =>
TreeSet.from(elements, Comparable.compare);
}
void main() {
TreeSet.fromComparable(['Hello!']); // OK, this works.
}
The main differences would be
new
.So the two approaches are not directly comparable, but there are are a whole range of reasons why this feature would enable certain things that we cannot do today.
I have on rare occasions wanted this. It's not unreasonable. But I suspect the value is marginal enough that it would be hard to justify the cost of the feature. As Erik notes, you can usually just use a static method, or maybe a subclass.
Currently the constructors a
class C<T> { ... }
must all accept all the same type arguments asC
.That sometimes leads code authors to restrict the type arguments programatically, like:
Here the goal is to have a constructor which only works with a non-nullable type. Similar problems occur for other subtypes than nullability like:
where you can omit the
compare
function when you know the elements are comparable, but we don't have intersection types, so we can't require elements to beList<E>&List<Comparable<E>>
and we can't requireE extends Comparable<E>
.So, what if constructors could provide type parameters that are more restrictive than for the class itself:
Type parameters on the class name of a constructor must be valid type arguments to the class itself, and they are implicitly applied. Inside the constructor, the type parameters of the class are not in scope, they're replaced by the type parameters of the constructor. (Or, rather, all constructors have type parameters, if you don't write them, they are copied from the class, and constructors always only see their own type arguments.)
If we ever add extension static members (#723), we might also allow extension constructors. At that point, it would likely mean that:
would be allowed as
List<int>.sumList([1, 2, 3])
andList<double>.sumList([1.5, 2.4])
, but notList<Object>.sumList(...)
. That means that we'd introduce the ability to have type-restricted constructors, but only through static extensions. We should then allow you to write the same constructors directly to avoid authors using extensions just for the added flexibility.