dart-lang / language

Design of the Dart language
Other
2.67k stars 205 forks source link

A extension (or static extension) can have an 'on-class'; how about an 'on-mixin', 'on-extension-type', etc? #4054

Open eernstg opened 2 months ago

eernstg commented 2 months ago

As proposed in https://github.com/dart-lang/language/pull/3835, an extension (or static extension) declaration can have an 'on-class', and it is able to "inject" static members or constructors into its on-class, and not into any other declaration.

class A {}

static extension ExtendA on A {
  A.name(int i): this();
  static staticMethod() {}
}

void main() {
  var a = A.name(3); // OK, same as `A()`.
  A.staticMethod(); // OK.
  ExtendA.staticMethod(); // Also OK.
}

The same approach would immediately be applicable to an enum, a mixin, a mixin class, and an extension type declaration. The mixin can't have a generative constructor, so we can't use a redirecting generative constructor in the extension, but that's just a normal error which is also applicable here.

I tend to prefer that we do not support the situation where the "on-class" is an extension or a static extension:

static extension E1 on String {}
static extension E2 on E1 { // Error.
  static void foo() {}
}

The reason for this is that E2 might as well have String as its on-class (calling the declarations of a static extension using the static extension itself as the syntactic receiver is expected to be a rare thing). That is, if someone wants to call E1.foo() then they might just as well call String.foo() (assuming we change E2 to have on-class String) or E2.foo(). Also, it doesn't make sense for E2 to inject any constructors into E1, they should just be injected into String in the first place.

@dart-lang/language-team, WDYT?

lrhn commented 2 months ago

Agree. Yes for types with associated static namespaces, no for extensions themselves.

I've considered "class type" to cover class, mixin and enum declarations, so those were already intended to be covered. Extension types were added later, but should also be allowed.

Since I prefer the form without a leading static, where all static members and constructors of extension declarations become static members of the corresponding on type's associated static namespace, I don't want to find a way to extend that to adding extensions to extensions.

The on type of an extension is a type. Extensions are not type declarations. They have a static namespace, but they are not themselves valid targets of a type extension, there is no on type matching them, so we'd have to invent new syntax. (Or at least new interpretation of on identifier).

If all members of extensions are accessible on the on type, then that is also the canonical way to access those members. Accessing them directly through the extension declaration (Ext<T>(value).instanceMember, Ext.staticMmeber or Ext<T>.constructor) is the fallback you use if you have namespace collisions. And possibly for private helper members.

So agree that you shouldn't add an extension to an extension, you should just extend the target type directly. Going through an intermediate extension is confusing, if it's even well-defined.

My head hurts trying to figure out what the applicability rules would be for an extension on an extension on a generic type. Don't make my head hurt.

leafpetersen commented 2 months ago

I wouldn't expect to be able to put an extension on an extension: extensions aren't types.