Description
Consider the following:
void foo<T>(T a) {
// ...
}
This code contains a top level function foo
with a single type parameter T
. It might look like T
doesn't have any behavior at all, but T
is implicitly declared to extend Object?
. Any value of type T comes with everything that comes with Object
e.g. equality, hashCode, an identity, runtimeType and much much more.
It could be argued that this is bad, but for purposes of practicality this seems to be a very good trade-off and I'm not arguing against that. However, I'd like to be able to specify and have the type checker statically prove that T
does not need to be anything as capable as Object
is.
This idea of a type not being an Object could seem to make no sense because everything in Dart is an Object. However, if we think of foo
as a specification and not as a program, T is simply overspecified. None of its Object
capabilities are being used in foo. They are redundant and not necessary.
Also, any reader of foo must for themselves first prove that e.g. the equality/hashCode/runtimeType/toString/... of a
is used/unused before he can assume that to be a fact when reasoning about foo. An empty top type would make cases where that is needed much less painful.
What I'd like to be able to do is something like the following:
void foo<T extends Top>(T a) {
// ...
}
where Top is e.g.
// dart:core
// Top is not an Object.
abstract class Top {}
class Object implements Top {
/// ...
external bool operator ==(Object other);
/// ...
i.e. bound T to a Type that has no methods or other properties and is not an Object.
One interesting consequence of that is that it would mean that T
could not be used as a key in Maps and could not be the value of Sets. Unfortunately, I think that Lists could also not contain values of type Top because they need equality for e.g. Iterable.contains
. But I claim that that is not a bad thing, but a useful feature. We would just need to be explicit about using Lists or Maps with T by e.g. injecting the construction of a List with T and using that instead.
void foo<T extends Top>(
T a,
List<T> Function() list,
) {
final newList = list();
newList.add(a);
// ...
}