Description
This is a proposal that we should allow a redirecting factory constructor of an extension type to redirect to a constructor of a type which is the representation type or some subtype thereof. For example:
class _C<X> {
final X x;
const _C(this.x);
X get value => x;
}
extension type C<X>._(_C<X> _) implements _C<X> {
const factory C(X x) = _C; // Currently an error; proposal is to allow it.
X conditionalValue(bool condition, {required X Function() orElse}) =>
condition ? x : orElse();
}
void main() {
C<num> c = C<int>(14);
num n = 2;
n = c.conditionalValue(true, orElse: () => n); // OK, yields 14.
}
The point is that there is no soundness related reason to not allow this, and it is helpful in the case where we wish to use the statically known receiver type to provide the value of a type argument like X
(which is often desirable for named arguments with the name orElse
;-).
In contrast, consider the following:
final class C<X> {
final X x;
const C(this.x);
X get value => x;
X conditionalValue(bool condition, {required X Function() orElse}) =>
condition ? x : orElse();
}
void main() {
C<num> c = C<int>(14);
num n = 2;
c.conditionalValue(true, orElse: () => n); // Throws!
}
In this variant, a run-time type error occurs because the argument passed to orElse
has type num Function()
, but the covariance based run-time type check requires an int Function()
. In this kind of situation, the covariance based run-time type check is actually harmful, because there is nothing in the logic of the given method that requires this function to have the more special type, it is all fully well-typed as seen from the call site and at run time when we rely on the statically known value of the type variable.
We could get a similar effect by introducing support for lower bounds on the type parameters of generic functions (see #1674), but this proposal seems simpler, and none of these proposals subsume each other completely (for example, the lower bounds feature wouldn't allow the C
extension type constructor to be constant).