-
Notifications
You must be signed in to change notification settings - Fork 205
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
Improve type inference for List (and maybe other generics). #980
Comments
Dart class generics is a dynamically checked covariant mechanism, which means that This is the same trade off between static and dynamic checks that Java and C# have for arrays (which may cause Over several years, Dart has changed in the direction of supporting purely static checking. In the specific area of class generics, we are likely to add support for declaration site variance (#524) and use-site variance (#753) to the language, which would make it possible for you to express the example in a way which has no dynamic checks (that is: it is statically type safe). class A {
A(this.name);
String name;
}
class B extends A {
B(String name): super(name);
}
void main() {
var list = generate();
list.add(A("Another A")); // Safe.
}
List<exactly A> generate() {
return [B("B 1"), B("B 2")]; // Inference makes it a `List<A>` by context.
}
// The previous version of `generate` needs to be adjusted:
List<exactly A> oldGenerate() {
var list = [B("B 1"), B("B 2")]; // type inference kicks in -> list is List<B>
list.add(B("B 3"));
return list; // Compile-time error: `List<B>` is not a subtype of `List<exactly A>`.
} This shows that you can restrict a legacy type like With declaration-site variance the variance would be made part of the class itself, which is more convenient and concise. However, it is likely to be a far too radical breaking change to make a class like So the response to this issue is basically: Yes, Dart class generics do currently rely on dynamic checks for non-covariant usages of type variables. Support for variance which relies only on static checks is coming. |
I'm not sure if I was clear enough. My problem is not with covariant generics but with a compiler not able to find out expected type.
will return
will return One more flutter example:
Adding following code to
But adding this one will lead to an error:
Why? Because both What is worse that previously working code like this one:
by a simple change (replacing SizedBox widget with a Container widget) can lead to a runtime error:
|
List<A> generate() {
return [B("B 1")];
}
List<A> generate() {
var list = [B("B 1")];
return list;
}
This is working as expected, but it may come as a surprise to you that inference in Dart gives priority to the context type (that is, the type required by the enclosing construct). The first return statement has In the second example, you create a list with no context type ( You cannot currently make With List<Widget> createFrom(List<MyDataItem> items) => items
.map<Widget>((item) => item.isText
? Text("Wow")
: Banner(message: "Banner", location: BannerLocation.bottomEnd))
.toList(); This will eliminate the subsumption where However, in order to support this property by a static check we'd need something like use-site invariance. The return type could then be changed to |
I'm not sure if I'm the only one who might be surprised to be honest.
I'm not saying it's not allowed. What I'm saying that compiler knows by function definition that it should return List and it's possible for compiler to enforce Looks like because my example was really small it's quite easy to say "works as expected". But in reality it can be much harder to argue/debug such behavior.
Does anyone expect this function to return different types? But it does. I cannot say how hard it is for a compiler in this case to take into account surrounding context like "
I know how to "fix" it as I mentioned in the original post. But do you think that everyone know/will be cautious enough to use the And to understand you better what's you saying is it's hard/impossible to improve type inference in this case or in your opinion everything works as expected? P.S.: |
@ayporos wrote:
Even if it might be possible, I don't think it is tractable or usable in practice to infer the type of a variable in a language with mutable state based on all its usages. It's been a firm rule for Dart type inference from the beginning that inference for a local variable can rely on the initializing expression, not all usages. Another matter is that a local variable may get a different type in a limited scope based on promotion (for instance Your examples keep confirming that statically checked variance will be a useful addition to Dart. The Kotlin example shows the same thing, because Kotlin variance is statically checked (it has to be, because there is no reification of type arguments at run time, so a dynamic check cannot be implemented). That, on the other hand, means that things like With statically checked variance in Dart, you can have the compile-time error as will as the dynamic test (using |
Sorry but I'm not following. My complain is not about variance but about type inference and type inference is static (is it not?). So basically why is it not possible to have a warning/error here:
same way Kotlin does it? Just so developer will be aware of possible problems? |
Here's the part where I explained why type inference for a local variable in Dart will not take all usages in the scope of the variable into account, it will only use the initializing expression:
The particular change to inference that might make a difference (and do something that matches your request), will not happen in Dart. It doesn't fit into a language with mutable state and subsumption, because it immediately gets intractable if you make an attempt to trace where each object goes, and then solve the type inference with respect to all the variables that may be involved. However, if you want static type safety (rather than a failing type check at run time), you need some form of statically checked variance. Kotlin variance is statically checked (and it's basically the same thing as Java wildcards, and they have the same property). As long as you have covariance for class type variables the |
Yes, this one I understand and I'm not argue with that.
I got your point.
or
And at the same time I find it hard to understand following code because of its implicit nature and no warnings from the compiler:
But from
looks like it is how it is. |
Thanks! An example like |
Type inference can be improved for Lists.
Let's take a look at the following code:
In this made up example you can see that actual type would be
List<B>
.Trying to add A object to this list will lead to
TypeError:..
As a proposals can it be that
List<A>
in this example same as function return type)OR
Real life example of flutter code:
Without changing
map
tomap<Widget>
one will not be able to add to this list later on anything except Expanded widget or its subtypes and it's actually hard to find out why (worse if code is not available to you)The text was updated successfully, but these errors were encountered: