Open Levi-Lesches opened 6 months ago
Using simply ClassName.instanceMethodName
to get a closure abstracting over a ClassName
would work today.
It won't work if we allow static and instance members with the same name in the same class.
Otherwise the idea isn't bad. Not sure how well it works for methods with parameters, though.
It won't work if we allow static and instance members with the same name in the same class.
Correct, this proposal kind of hinges on that: ClassName.methodName
is always a shorthand for the corresponding instance method name and is therefore reserved. It's not breaking currently, but it does restrict future possibilities. I think this is a good use for it, as it would be pretty confusing if ClassName.methodName
was too different than instance.methodName
Not sure how well it works for methods with parameters, though.
I left them out intentionally because they certainly do complicate things. In general, you'd probably be safe if you said "let the instance be the first positional parameter, just like Python's self
". But I'm indifferent to it, because the main purpose of this proposal is to allow for more convenient tear-offs, and you're unlikely to get a useful tear-off out of this -- unless you specifically have a closure of the form (a, b) => a.method(b)
,
@Levi-Lesches This feature request should effectively add the only method reference expression Java has and Dart doesn't yet, right?
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Sounds like it!
I have many times wished there was a way to get a closure that calls an instance method with a given parameter. Tear-offs do the opposite: they partially apply the receiver but not the arguments. I fairly frequently wish to partially apply the arguments but not the receiver.
This proposal would give you a way to do that.
I like it. As @lrhn notes, we could actually use Foo.bar
syntax for this, since we don't allow instance and static member names to collide anyway. If you do Foo.bar(...)
and bar(...)
is an instance method, it's currently just an error. We could instead interpret it to mean create a closure that partially applies the given arguments but not the receiver.
I don't know how comfortable I am with doubling down on the restriction that you can't have an instance and static method with the same name. We might want some slightly different syntax, like Foo#bar(...)
or Foo::bar(...)
. Of course, syntax is always hard. :)
since we don't allow instance and static member names to collide anyway.
For now. You can have something like both if you can live with the instance member being an extension member. If we get static extensions, you can also add the static member on the side, which may be more common.
class Color {
final int red, green, blue;
const Color(this.red, this.green, this.blue) : assert(_isValid(red, green, blue);
}
static extension Colors on Color {
static const red = Color(255, 0, 0);
static const green = Color(0, 255, 0);
static const blue = Color(0, 0, 255);
// others
}
void main() {
print(Color.red.red); // 255
}
But that's a workaround for what the user actually wants, which is to have a static and an instance member with the same name. At that point, it feels like we should just allow you to declare those two members in the same class. (That is: I expect a consequence of static extensions to be that we'll want to remove that restriction, and doubling down on it would then indeed be a bad idea.)
In Python, any instance method is really a static method with a
self
parameter, and the object you call the method on is implicitly passed first to this method. For example:In Dart, we often like to use tear-offs instead of passing closures around. A useful example is the following:
However, it is no longer possible to pass a tear-off if we make the greeting an instance method:
My feature request is to implicitly (or on-demand) make available a static method that's equivalent to the Python version of every instance method. Not advocating for instance methods to be replaced by static methods. But, if there's an instance method named
A.b()
, then there should be a static methodA.$b(A a)
, and it should be possible to refer to it as justA.b
as syntax sugar. In other words:The semantics seem to be well-defined as far as I can tell. The alias of
User.makeGreeting
forUser.$makeGreeting
is safe because there cannot be a static member with the same name as an existing instance member, and using an instance member without an instance is always an error. In other words, the alias strictly adds functionality and doesn't break any existing code or create conflicts. Additionally, the compiler may only decide to generate these if they're directly referenced in the first place. This would allow us to use tear-offs way more frequently, especially in JSON contexts where(obj) => obj.toJson()
is quite common.Edit: One specific case has been pointed out to me where this could cause a conflict:
In cases like these, where
ClassName.methodName
is already a valid identifier and would thus cause a conflict, I would say to not generate the implicit static wrapper, to avoid breaking existing code and causing "magic" behavior. But I'm open to whatever makes more sense on a case-by-case basis as well.