Open
Description
This has been discussed periodically both in the issue tracker and in person, but I don't think there's a tracking issue yet. (EDIT: Here is the original issue from 2011 - dart-lang/sdk#49).
Now that we're moving to a sound type system, we have the ability to overload methods—that is, to choose which of a set of methods is called based on which arguments are passed and what their types are. This is particularly useful when the type signature of a method varies based on which arguments are or are not supplied.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
jodinathan commentedon Sep 29, 2017
any news on this?
maybe with dart 2.0?
=]
lrhn commentedon Jun 22, 2018
Not in Dart 2.
This is a significant change to the object model of Dart.
Currently, a Dart object has at most one accessible member with any given name. Because of that, you can do a tear-off of a method.
If you could overload methods, tear-offs would no longer work. You would have to say which function you tore off, or create some combined function which accepts a number of different and incompatible parameter signatures.
It would make dynamic invocations harder to handle. Should they determine that method to call dynamically? That might cause a significant code overhead on ahead-of-time compiled programs.
I don't see this happening by itself. If we make a large-scale change to the object model for other reasons, then it might be possible to accommodate overloading too, but quite possibly at the cost of not allowing dynamic invocations.
jodinathan commentedon Jun 22, 2018
but with a sound dart we don't have dynamic invocations, do we?
eernstg commentedon Jun 22, 2018
We can certainly still have dynamic invocations: If you use the type
dynamic
explicitly and invoke an expression of that type then you will get a dynamic invocation, and it is an important part of the Dart semantics that we have enough information available at run time to actually make that happen safely (that is, we will have a dynamic error if the invocation passes the wrong number of arguments, or one or more of the arguments has a wrong type, etc).Apart from that, even with the most complete static typing you can come up with, it would still be ambiguous which method you want to tear off if you do
x.foo
andfoo
has several implementations. So it's more about first class usage (passing functions around rather than just calling them) than it is about static typing.matanlurey commentedon Jun 26, 2018
@lrhn:
You already cannot tear off what users write instead of overloads, which is multiple methods:
... so given that overloads would be sugar for that, I don't see it any worse.
@eernstg:
Is it being dynamically invokable a requirement? I don't think it is.
I'd heavily like to see a push for overloads in the not-so-distance future. My 2-cents:
(@yjbanov and @srawlins get credit for parts of this discussion, we chatted in person)
Proposal
Don't allow dynamic invocation of overloaded methods
... or limit how they work:
If you wanted to be real fancy (@munificent's idea, I think), you could have this generate a method that does dynamic dispatch under the scenes. I'm not convinced this holds its weight (and makes overloading, which should be a great static optimization a potential de-opt), but it's an idea.
I realize this adds a feature that is mostly unusable with dynamic dispatch, but Dart2 already has this issue with stuff like reified generics.
Consider this very common bug:
Limit tear-offs if the context is unknown
Rather, if the context is ambiguous, then make it a static error.
... another option is have
var bar = foo.bar
basically generate a forwarding closure (a similar de-opt to the dynamic dispatch issue). Again, not my favorite, but I guess no more bad than code already being written.Side notes
Let's consider how users are working around this today:
Object
ordynamic
withis
checks and optional arguments:I think the idea for overloads is no worse than 2, and you can still write 1 if you want.
EDIT: As @srawlins pointed out to be, another huge advantage of overloads over the "dynamic"-ish method with
is
checks is the ability to have conditional type arguments - that is, type arguments that only exist in a particular context:It's not possible to express this pattern in dynamic dispatch (or with a single
bar
at all).matanlurey commentedon Jun 26, 2018
By the way, this would have solved the
Future.catchError
issue:... as a bonus :)
eernstg commentedon Jun 29, 2018
@matanlurey,
That was actually the point I was making: It is important that there is a well-defined semantics of method invocation, and if just one static overload is allowed to exist then every dynamic invocation will need to potentially handle static overloads, and that presumably amounts to multiple dispatch (like CLOS, Dylan, MultiJava, Cecil, Diesel, etc.etc.), and I'm not convinced that it is a good trade-off (in terms of the complexity of the language and its implementations) to add that to Dart.
In particular, the very notion of making the choice among several method implementations of a method based on the statically known type is a completely different mechanism than standard OO method dispatch, and there is no end to the number of students that I've seen over time who just couldn't keep those two apart. (And even for very smart people who would never have a problem with that, it's likely to take up some brain cells during ordinary daily work on Dart projects, and I'm again not convinced that it's impossible to find better things for those brain cells to work on ;-).
matanlurey commentedon Jun 29, 2018
@eernstg:
Why? If we just don't allow dynamic invocation to invoke static overloads, nothing is needed.
I just want what is already implemented in Java/Kotlin, C#, or other modern languages. Do they do something we aren't able to do, or is this just about preserving dynamic invocation? As I mentioned, the alternative is users write something like this:
Not only do we punish users (they have to name and remember 3 names), dynamic invocation cannot help you here (mirrors could of, but that is no longer relevant).
nex3 commentedon Jun 29, 2018
It's worth mentioning that if we decide to support overloads without dynamic invocations, this means that adding an overload to an existing method will be a breaking change--one that probably won't be obvious to API designers.
matanlurey commentedon Jun 29, 2018
Depending how we do it, we theoretically could support a dynamic fallback overload:
It's not clear to me this is particularly worth it, though. Other hotly requested features like extension methods would also suffer from being static only, and changing a method from invocation to extension would be a breaking change.
nex3 commentedon Jun 29, 2018
I expect it won't be too surprising to users that changing an existing method is a breaking change. Adding a new method being a breaking change, on the other hand, is likely to be very surprising, especially since it's safe in other languages that support overloading.
matanlurey commentedon Jun 29, 2018
Right, because they never supported dynamic invocation (or only do, like TypeScript).
One project @srawlins was working on back in the day was a tool that could tell you if you accidentally (or on purpose) introduced breaking changes in a commit. I imagine a tool could help, or we could even add a lint "avoid_overloads" for packages that want to be dynamically-invoke-able.
nex3 commentedon Jun 29, 2018
Users aren't going to know to run a tool to tell them that overloads are breaking changes any more than they're going to know that overloads are breaking changes. And even if they did, the fact that adding an overload requires incrementing a package's major version would make the feature much less useful for anyone with downstream users.
I don't think a lint would do anything, because upstream API authors don't control whether their downstream users dynamically invoke their APIs. In fact, since we don't have robust and universal support for
--no-implicit-dynamic
, the downstream users probably also don't know when they're dynamically inovking APIs.matanlurey commentedon Jun 29, 2018
OK, I think we can note that this feature would be breaking for dynamic invocation and leave it at that.
The language team hasn't given any indication this particular feature is on the short-list for any upcoming release, and I'm assuming when and if they start on it we can revisit the world of Dart (and what support we have for preventing dynamic invocation entirely).
I would like to hope this issue continues to be about implementing the feature, not whether or not it will be a breaking change (for all we know this will happen in Dart 38, and dynamic invocation has been disabled since Dart 9).
EDIT: For anyone reading this, I am not saying that will happen.
munificent commentedon Jul 4, 2018
I also think overloading would be a fantastically useful feature, but it's complexity is not to be under-estimated. If the language folks seem to cower in fear every time it comes up, that's not without reason. This tweet sums it up pretty well:
123 remaining items