-
Notifications
You must be signed in to change notification settings - Fork 220
Description
It can be useful to test whether a type satisfies certain constraints, and when more than one type satisfies them: how this particular type satisfied them. Here are a couple of examples:
In response to #40 about adding functionality to existing types, scoped static extension methods were proposed in #41. They call for a mechanism that will enable the specification of certain constraints on types as well as the ability to declare type variables that are bound to specific types as a result of the procedure whereby it is verified that the constraints are satisfied. The basic idea is that an extension is applicable to a given expression if the static type of that expression matches the extension, and the matching procedure can make such things as the type arguments of a class available for the extension methods. The specification of static extension methods is still under construction, but here's an example illustrating the idea:
// Example from scoped static extension method proposal.
extension ListFancy<T> on List<T> {
List<T> copyReversed() => List<T>(this.length)..setAll(0, this.reversed);
}
In this example, the extension method mechanism could be specified such that the type variable T
is bound to the value of the actual type argument to the dynamic type of the list denoted by this
, which enables the creation of a list with the same type argument in the body of copyReversed
. Alternatively, the mechanism could be specified such that T
is the static type of the expression which is the receiver of the invocation of copyReversed
.
The language team has previously discussed mechanisms for performing an 'existential open' operation (coming up, e.g., in dart-lang/sdk#31953 (comment) and dart-lang/sdk#33841 (comment)), in which the ability to introduce new type variables and bind them to values obtained by inspecting the dynamic type of a given instance is the main purpose of the mechanism. Here is an example of how that mechanism could be used:
main() {
List xs = ...; // Some non-trivial computation, such that the exact type is not known.
if (xs is List<?X>) {
// Here, `X` denotes the exact type argument of the
// dynamic type of `xs` at `List`.
List<X> ys = [];
ys.add(xs[0]); // No dynamic check needed.
}
}
In this example, it is tested whether there exists a type S
such that xs
has type List<S>
, and if this is true then the type variable X
is introduced into the scope of the following block {}
, bound to S
.
The general point here is that it can be useful to perform a check, at compile time or at run time, of whether a given type satisfies some constraints which may be expressed in a form which is itself similar to a type, and also to introduce and bind certain type variables to types in order to provide information about how those constraints were satisfied.