-
Notifications
You must be signed in to change notification settings - Fork 213
Function Types Do Not Support Dynamic #4292
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
Comments
This was how The reason is that Dart 2 made a distinction between dynamic invocations and typed invocations, distinguished at compile time, and only dynamic invocations need to do runtime type checks at the call point. (Or at least type checks that are not statically known.) In Dart 2, Can't the compiler just check at the point where it casts a type that contains class Caster<Sup, Sub extends Sup> {
Sup cast(Sub f) => f;
}
void Function(String) strCast(
void Function(dynamic) f) =>
Caster<
void Function(String),
void Function(dynamic)>()
.cast(f); These functions only do up-casts, and there is no place where a That means that if we allow casting Neither is a good option. |
Thanks for the explanation. I am having difficulty conceptualizing this
I don't understand why the design is to treat |
An up-cast is a cast to a supertype. That's generally safe to do because an instance of the subtype must be an instance of the supertype. So no runtime checks are needed or done for up-casts. This issue requests that you can freely "cast" a function type to a subtype, from As I read the request, it would not be blindly treating a There are different ways to do that:
So:
If we introduce this feature, yes. In some way. The example here does a number of assignments, but let's boil it down to a simpler example. void Function(int) f = (int x) {}; // Same type, completely safe.
void Function(dynamic) d = f; // Proposed allowed.
var dlist = <void Function(dynamic)>[d]; // Same type, should be allowed, right?
List<void Function(String)> slist = dlist; // Up-cast! Should be safe.
void Function(String) g = slist.first; // Same type. The first line is trivial, the second line is what is proposed to be allowed. Every operation here, other than the second line, is an assignment where the current language knows there is no need to do a type check, because it's assigning an expression with a static type to a variable with a declared type that is a supertype of (or the same as) that static type. In a sound language, it cannot fail. But it's not sound because it ends up assigning a Where would you insert an extra check, which would throw in this case, to make this code sound? To make this possible, it has to either change what |
The question is not why this is an error, but why this is a runtime error. main() {
final f=(final num n)=>print(n);
(f as void Function(dynamic))(5); // runtime error. Why not a static error?
5 as String; // not a static error either
5 as int; // Warning: unnecessary cast
} It seems Sometimes, the situation is reversed: the compiler knows for sure that the runtime check would be redundant, but there's no mechanism for the compiler to tell the runtime: don't check the type; I guarantee it's correct (that's what I gathered from the parallel thread). The check can be quite expensive, especially in the case of a function parameter. My speculation is that the phenomenon is a heritage of dart 1, where the static types were not even preserved in runtime. |
I understand this, for example
This looks good to me, the annotated type is the strongest type of the right-hand-side.
Yes, this is what I would like i.e.
This looks good to me, the list's type parameter is identical to the item.
I believe this a part I did not understand prior to your example. If I understand correctly now, then this is an up-cast because normally whatever a function can do to a supertype it should be able to do to any of its subtypes, since any subtype is guaranteed to have that supertype's API. I believe I was getting confused because normally changing a subtype to its supertype in terms of extends/implements/mixin hierarchy is an up-cast. The only way to avoid a breaking change would be to introduce another type that functions like void Function(int) f = (int x) {}; // Same type, completely safe.
void Function(_) d = f; // Proposed allowed.
var dlist = <void Function(_)>[d]; // Same type, should be allowed, right? But from there up-casting is not permitted (unless there is a way to reasonably allow if (widget is RadioListTile<_>)
{ widget.onChanged; // Accessing the member won't crash because there is no type issue
} Or would this be pointless compared to the solution which ended up working: if (widget is RadioListTile)
{ (widget as dynamic).onChanged; // Accessing the member doesn't crash
} |
This is derived from @MikePendo's issue dart-lang/sdk#60288, a practical instance of this problem being encountered.
Context
Suppose one had a function
final f=(final num n)=>print(n);
. The type isvoid Function(num)
.int
is a subtype ofnum
so the function can be casted to avoid Function(int)
as expected. This is good.If the types were reversed, we have
final f=(final int i)=>print(i);
of typevoid Function(int)
. It is not a subtype ofvoid Function(num)
, even thoughint
is a subtype ofnum
. This is because the function may rely on behaviour specific toint
in its execution, and therefore cannot be generalized. Again this is good.Problem
Suppose we again have
final f=(final int i)=>print(i);
of typevoid Function(int)
. I may want to type-erase the parameters to have code that handles a general kind of n-ary function (in this case, a single input one). I expect the function to be castable tovoid Function(dynamic)
because whether the function will work or not will be determined at runtime bydynamic
computations. However, it is not castable! I cannot dof as void Function(dynamic)
, it throws an exception.This is (1) not intuitive because
dynamic
should work and be resolved at runtime as it does everywhere else, and (2) inconvenient for generic situations, requiring less safe code to be written which can therefore nonnecessarily lead to more errors. Again, see @MikePendo's issue dart-lang/sdk#60288 for a practical instance of this problem.Solution
Allow function types to have input, and output parameters type-erased as
dynamic
. For example,String Function(int, bool)
should be able to be casted to any one of the following:dynamic Function(int, bool)
void Function(dynamic, bool)
dynamic Function(dynamic, bool)
dynamic Function(dynamic, dynamic)
The text was updated successfully, but these errors were encountered: