dart-lang / language

Design of the Dart language
Other
2.61k stars 200 forks source link

Static+Instance methods #2760

Open FMorschel opened 1 year ago

FMorschel commented 1 year ago

This is a new feature suggestion. I haven't seen any similar issues, but I don't know how to name this to search in other ways.

I know Dart has (as of right now) no way of letting two methods with the same name be in the same scope. One way around this is to create a static and an extension method.

Most of the time, when creating methods with the same name, at least when I and my colleagues do, they are meant to do the same thing.

I was thinking if there could ever be another word like self (similar to python) that we could put inside the parameters, of, let's say a static method, and that would mean something like:

RemcoSchrijver commented 1 year ago

Do you perchance mean method overloading? If yes, there is an issue on that already: #1122

FMorschel commented 1 year ago

Something like that, but my intention with this specific issue was to discuss whether this "self" word could be created and if it would be useful in any way.

This automatically would create an instance and a static method with the same name, that would do the same process, but if called from the static method, it would need an object from the class, if called from the instance it would give itself as the parameter.

mateusfccp commented 1 year ago

I think this can be easily solved once we have static metaprogramming.

lrhn commented 1 year ago

The effect would be like if you could have both a static and an instance member with the same name, and either you did:

class Me {
  R foo(P v1, P v2) => Me.foo(this, v1, v2);  
  static R foo(Me self, P v1, P v2) { actual method }
}

or

class Me {
  R foo(P v1, P v2) { actual method }  
  static R foo(Me self, P v1, P v2) => self.foo(v1, v2);
}

It's probably the former, because then the static method won't change behavior when called with a different implementation of Me.

This also shows how the extension method works:

class Me {
  static R foo(Me self, P v1, P v2) { actual method }
}
extension OverloadMe on Me {
   R foo(P v1, P v2) => Me.foo(this, v1, v2);  
}

Because it's not declared in the same lexical scope, it doesn't conflict with the static method.

I'm not sure this particular use-case is that compelling to me. I am aware of other languages which has this feature, but it's not a good match for Dart. Because Dart classes expose implementable interfaces, every class can have a subclass which doesn't inherit implementation. That means that the static method taking a Me object as argument can only use the Me as an interface (same for an extension method, which is really a static method that takes this as an implicit argument). An instance method can refer to its actual superclass and do super.foo() invocations.

We cannot simply allow instance methods to be treated as static methods that take an extra self argument, because then instance methods can no longer know their superclass. So we'd at least have to have some kind of opt-in syntax to make a method usable as both a static and instance method.

I'd rather work on allowing declaring both static and instance members with the same name in the same class. Qualified access is never ambiguous, you either write Me.foo for the static member or meObject.foo for the instance member. The only ambiguity is an unqualified foo written inside the same class, since you can still write Me.foo or this.foo, we could just force you do disambiguate. Or we could have defaults, like in a static member body, foo always refer to the static member. In an instance member, we could have either default or force you to disambiguate. (I think we'd have to look at use-cases before making a decision, but requiring you to disambiguate is the safe choice, and we can always loosen that restriction later if we get data pointing in either direction.)

If we also introduce concise method forwarding syntax, then it might be as easy as:

class Me {
  static R foo(P v1, P v2) { actual method }
  foo(...) := Me.foo(this, ...); // Very hypothetical forwarding syntax.
}
RemcoSchrijver commented 1 year ago

Wouldn't it be possible to disambiguate by looking at the respective signature for every method like seen with overloading. I doubt that there are many cases where two methods with the same name, where one is static, has the exact same signature? Then the need to force to developer to disambiguate is only needed in the special case where the signatures are equal?