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
Functions downcast #362
Comments
Would this be an automatic wrapping (an The former choice means that the function changes identity. The wrapper function is created implicitly on each assignment, and will only be equal, not identical, to another wrapping of the same function. The latter choice may have wide-ranging side effects. We currently require a subclass method's function type to be a subtype of the superclass method's function type. If we extend the subtyping relation this way, then class C {
int foo(int x, String y) => 0;
}
class D extends C {
int foo(int x) => 0;
} would become valid. We probably do not want that, it's too error-prone. That all suggests that we are talking about implicit wrappers/adaptors. The danger of allowing such an implicit conversion is that it might mask errors. |
I would say that
I don't see any reason for this example to be valid. class C {
int foo(int x, String y) => 0;
}
class D extends C {
int foo(int x, String y, [double z]) => 0;
} On the other hand, the following may become a valid scenario: void Function() cb;
if (cb is void Function(int)) {
cb(42);
} Methods excluded, I think that the potential mistakes are very rare, and the gain probably outweigh the loss. |
If an That is why So, if you now allow class C {
int foo(int x, String y) => x;
}
class D extends C {
itn foo(int x) => x;
} should be valid if subtyping is the only constraint. After all: int Function(int, String) foo = D().foo;
foo(42, "yep"); is valid, and so is: (D() as C).foo(42, "yep"); It works somewhat like a covariant override in that Interestingly, this means that Which means that the type system breaks down and becomes unsound: int Function(int) f1 = (int x, [String s]) => x + (s?.length ?? 0); // valid by current type system.
int Function(int, int) f2 = f1; // new subtyping rule.
f2(1, 1); // run-time type error because an int is not a string. So, using subtyping is not going to work. The latter scenario is already valid, and the test is satisfiable: void Function() cb = ([int x]) {};
if (cb is void Function(int)) {
c(42);
} This runs today. |
Sorry, I got confused. But yeah, that's now going to work as I expected. The assignment won't do. |
Consider the following definitions:
Currently, we cannot assign the function
foo
toMyCallback
and instead have to write such closure:This proposal is to allow such assignment:
Reasoning
While the current behavior is technically correct, it makes adding parameters to a callback a breaking change.
And at the same time, it is common to not care about some parameters passed to a callback (hence why we have
_
).There are many examples where such a feature may be useful.
For example,
Iterable.map
/Iterable.forEach
may want to pass the index of the item to the callback as a second parameter:Another example is the optional
child
argument on theBuilder
pattern of Flutter widgets.Some widgets like
AnimatedBuilder
orValueListenableBuilder
takes an optionalchild
for performance optimizations like so:But some widgets such as
StreamBuilder
currently do not offer suchchild
argument – which makes them inconsistent.Ideally, these widgets should take an optional
child
, but again that is a relatively significant breaking change.With this proposal, the changes described above would not be a breaking change any more.
The text was updated successfully, but these errors were encountered: