language icon indicating copy to clipboard operation
language copied to clipboard

Enhance override inference to infer type parameter bounds

Open eernstg opened this issue 6 months ago • 0 comments

We currently support a kind of type inference known as override inference whereby some types in a function signature that are omitted in a declaration can be obtained implicitly by looking up the corresponding types in one or more overridden declarations.

This issue is a proposal that we should support a similar mechanism for the bounds of type parameters. In the example below I've used abstract declarations everywhere, because it's less noisy, and function bodies would not make a difference.

abstract class Other {
  Object? m3();
  String? m4(dynamic _);
}

abstract class A {
  void m1(int i);
  num m2({String s = 'default'});
  dynamic m3();
  String m4(Object _);
  X m5<X extends A>(X x);
}

abstract class B implements A, Other {
  m1(_); // OK, inferred as `void m1(int _)`.
  int m2({s = 'new default'}); // OK, override some parts and infer others.
  m3(); // OK, `dynamic` and `Object?` resolved as `Object?`.
  m4(_); // Error, no superinterface signature for `m4` is most specific.
  
  // Proposal: This is OK, too, inferred as `Y m5<Y extends A>(Y x);`
  Y m5<Y>(x);
}

Override inference needs to preserve the structure of the bounds by performing renames (so m<X extends Comparable<X>>() would give rise to inference of m<Y>() as m<Y extends Comparable<Y>>()). We could also recommend that the type parameter names are preserved, but that's a style issue rather than a hard technical requirement.

We could even allow the type parameter declarations to be omitted entirely (using <> to say that this method is generic, or even omitting the angle brackets), in which case all other parts of that method declaration must be expressible with no reference to any type variables.

abstract class C {
  X foo<X extends Comparable<X>>(X x);
}

abstract class D implements C {
  foo<>(x) => x; // Or perhaps even `foo(x) => x;`
}

These enhancements to the override inference mechanism share a property with the current override inference mechanism: They both allow us to omit API related information from a declaration. Hence, they both make it harder to determine the actual signature of the declaration (unless we're using an IDE that shows us the result of override inference), which may be considered to be a step backwards for code readability.

However, we support override inference in its current form, and we will support it even more wholeheartedly for method declarations marked as override, and the omission of type parameter bounds does not seem to introduce any kind of readability issues that differ from the ones that we already accept for the parameter types and the return type.

In other words, we have already decided that it may be a good choice for developers to write code where some method signatures are implicit, because conciseness and writability is more important than visible, local documentation in this case. In particular, this may be a good trade-off in the situation where the code is basically providing implementations of types declared in a widely used framework/package/library M: Everybody will look at the documentation of M in order to understand how to use this API anyway, and the code that omits parameter/return/bound types is just providing an implementation, it doesn't intend to say anything new about the APIs.

eernstg avatar Aug 15 '24 10:08 eernstg