-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle covariant callbacks #27487
Comments
How does the return type of the factory function type vary? typedef T GestureRecognizerFactory<T extends GestureRecognizer>(T recognizer); or some other generic function type? Or is there no structure to how the argument and return type vary? One solution I see is to just wrap the function in a class. class GestureRecognizerFactoryBox<T> {
final GestureRecognizerFactory<T> _factory;
GestureRecognizerFactoryBox(this._factory);
T create(T recognizer) => _factory(recognizer);
} Then the class is correctly typed, and because it's a class type, not a function type, it allows covariant subtyping. That means that you can store a Alternatively, wrap the function when it's added to the map: void addGestureRecognizerFactory<T extends GestureRecognizer>(Type<T> type,
T factory(T recognizer)) {
_map[type] = (GestureRecognizer r) => factory(r as T);
} It sucks somewhat to have to make a class instance to keep a single function, but the existing code is type-unsafe as written, and we don't allow type-unsafe code. I don't think we should compromise that for code like this when there are simple workarounds. Alternatively, don't use a plain normal map, use a specialized class with type-aware |
Yes, that would be an accurate description of the constructors. The reason we don't do that is that there's no way to specialise the type per entry in the map, so there's not much gain to be had from it.
The ability to write this kind of code is part of what made Dart 1.0 great. I would be very sad to lose it. |
Yup. I fleshed out a proof of concept of that here: https://github.com/munificent/flutter/commit/8def6c700d996efd1aa3c81be620555bf6001529 |
status: under discussion. |
I think if we had true generic methods (i.e. generic methods were at runtime we could inspect the type argument) then we could rewrite this code to not require covariant callbacks. |
Not planned for 1.50. |
@cbracken ran into a case today where he wanted to say |
cc @matanlurey we've discussed this one a lot 😄 |
With In order to ensure expression soundness we give a tear-off of such a method the reified parameter type However, we did not allow However, we could certainly allow functions in general (including function literals) to obtain the same kind of flexibility, using the same syntax:
The underlying principle is that function types (static and dynamic) have no idea that any particular function has a covariant parameter, it's completely an implementation detail for that function: It announces (in its dynamic type) that it will accept arbitrary arguments ( The static type preserves the declared type of the covariant parameter. This basically prevents "stupid" invocations where we can predict that the dynamic check will fail (like So if you actually want |
Flutter has a few cases where they are trying to use functions in a way that is statically unsound. Almost all of them are around
GestureRecognizer
and look roughly like:Here, that map set isn't sound because the function literal takes a
TapGestureRecognizer
, not aGestureRecognizer
. The former is a subtype of the latter, so this isn't statically safe.Before Flutter can become strong mode clean, we need to address this. We need to decide how/if we want to change the language to handle code like this. And then, once we have, we need to implement and spec it.
The text was updated successfully, but these errors were encountered: