-
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
Generics & Types that implement interfaces #2570
Comments
You cannot call static methods on type parameters like Static methods belong to the namespace introduced by the class/mixin/extension declaration they are inside, but not to the type. Dart is statically typed. Since static members are not part of any type or interface, and are not inherited in any way. There is no way to statically know whether a static Even if we allowed you to invoke a static method from the declaration of the type bound to It's not easy to implement. I would defer to #356. |
Thanks for the response, Lasse. May be I am wrong, but T.fromString is just a template. Compiler will need to know only when the template is used somewhere, and there it would/can know if the type invoking the template has fromString or not. Also not enforcing static members in subtypes feels very wrong and root of all these problems. It seems this is more of a prioritization issue, but to me it also seems like a language design issue. Something is not feeling right with the language as it did with C++, Java & Python as I learnt them. Here the language seems messed up. Java was pain to code in, but didn't feel messed up in design. I hope it doesn't go to dustbin because of these issues. There are so many competitors in market today. Again, really sorry for the rant, but am trying to iron out Future/then(onValue, onError)/value/error/catchError since morning. Finding random behaviour at different places. Never had that in any other language. Going after the mystical FutureOr now. Hopefully I get a hold of what really is going on there. |
Consider the following: class A {
A.fromString(String _);
}
class B extends A { /* Notice the lack of .fromString constructor */ }
T parseData<T extends A>(String data) => T.fromString(data);
void main() {
A a = parseData<A>("hello"); // okay, calls A.fromString("hello")
B b = parseData<B>("hello"); // B extends A but does not have a .fromString!
} The compiler can see that this is a problem and tries to warn you: Just because
This is a matter of getting to know the language. For someone who doesn't know
The semantics of Future are pretty precisely defined in the documentation and are overall pretty similar to JavaScript's // pretend this does real stuff
Future<void> connectToServer() => Future.delayed(Duration(seconds: 1));
// instead of
Future<String> getData() {
return connectToServer().then((_) => "Data").catchError((error) => "Error");
}
// try this
Future<String> getData() async {
try {
await connectToServer();
return "Data";
} catch (error) {
return "Error";
}
} |
It's not. Instead it treats generic code as being actually generic, aka. working the same for all possible type instantiations. In Dart, a type parameter represents an actual runtime argument to the generic thing. It exists at runtime (unlike Java, which erases type arguments). A generic class which is instantiated carries along a concrete representation of the type argument, as if it was an instance member of the object. There is only one class, no templating, and that class has a type argument as part of its state. A generic function can access the concrete type argument passed to it at runtime (and can pass it on to other functions or class instantiations). There is only one function body, no templating, and the function body can access the type argument passed to it. Because of that, and Dart being statically typed, the only thing you can do with a type parameter is what you can do with all types. That's is to do subtype tests and use as a type argument again. Dart is different from most other, otherwise similar, languages precisely in that it keeps the type arguments at runtime. That sometimes get in the way, like Future<int> f = Future<num>.value(1);
// or
List<int> l = <num>[1]; Those assignments are not allowed. Even though the created future/list definitely contains just integers, it's still a But it also allows you to do something like: class Collector<T> {
List<T> values = [];
bool tryAdd(Object? value) {
if (value is T) {
values.add(value);
return true;
}
return false;
}
void main() {
var ints = Collector<int>();
var strings = Collector<String>();
var nulls = Collector<Null>();
sort([ints, strings, nulls], [1, "a", 2, null, "b", null, 3.5]);
print(ints.values); // [1, 2]
print(strings.values); // [a, b]
print(nulls.values); // [null, null];
}
void sort(List<Collector<Object?>> collectors, List<Object?> values) {
for (var value in values) {
for (var collector in collectors) {
if (collector.tryAdd(value)) break;
}
}
} (I'm sure it's possible in some other languges, but not in Java, and likely not in C#). For now, accept that Dart is different. You cannot assume that you can carry over behavior from, e.g., C++ to Dart (or to Java or C# for that matter). I believe C# is closer to C++ in that it does do template instantiation, but mainly as an implementation trick. It also tries to be statically sound, which is why they're now introducing static virtual members. Dart would need something like that too, to support calling methods on type variables. |
Ah! This is what I was missing. Thank you from the bottom of my heart. I assumed that with all the "type checking", templating would be happening in the background.
... and that is a design choice I guess, which, for now, is making me very uncomfortable. As I am imagining this with all the other choices Dart has made, and looking at your examples, I am thinking that this doesn't seem to go well with the love of programming. Basically it feels like Dart language designers are not thinking like users of language but just designers of language, focusing too much on tough optimization and patting their own backs. In this specific case, I strongly think that this should be a runtime-error, not compile-time error, at worst. If the programmer is already mentioning extends/with/implements, what is the compiler trying to save the programmer from? (Her/Him)self? No smart language should try to do that. I would say this is a bad choice made by dart-lang-designers. If it has come to this then may be it is time to rethink the set of design choices. Again thanks for the detailed explanations, and it is early days for me with Dart, but I have already discovered many weird asks from programmers by designers (like requiring to mention void in some places and not others). Thanks as well for the suggestions on onError/FutureOr/catchError. I was able to conquer that mess and boy it is a mess. Will post some bugs/suggestions around it after rethinking along the points made by you in your replies above. |
The one feedback we have consistently received from our users so far, is that they prefer compile-time errors over runtime errors. Dart has moved quite some way in that direction. Dart 1.0 was much, much more dynamic and made most things runtime errors. You could get away with anything. Even checking the types of variables was optional. If anything, retaining type arguments at runtime was a consequence of that choice, because Dart 1.0 didn't use static types for much, or anything unless you asked for it. Checking them at runtime was all you could do (if you asked for it). If you consider the goal to be "no runtime errors", then we're still some way off. Dart is not Haskell or SML, it doesn't necessarily run just because it compiles. However, the unsafe places are well known and statically detectable, which is why Dart can have a fairly efficient sound runtime model, because it knows where to insert the checks that prevent unsoundness, and where it can just let well-typed values pass through. |
Please check the code at here
On line 13, the compiler reports the following error:
The method 'fromString' isn't defined for the type 'Type'.
Try correcting the name to the name of an existing method, or defining a method named 'fromString'.
I have tried to replace "extends" with "with" or "implements". Compiler doesn't like "with" or "implements". "extends" it tolerates.
I have tried mixins, abstract classes...
Yeah, won't work. That is trying to attack at the wrong place.
Because the error says "isn't defined for the type 'Type'". It just goes to "Type", checks its members, doesn't find "fromString", complains. Compiler doesn't see that this type may have static methods which are guaranteed to there because of "extends/with/implements". I would prefer "implements".
I think this should be easy to implement. Please enlighten me if I am wrong.
May be this is a duplicate of #356, but the discussion there seems to be going in multiple directions.
The text was updated successfully, but these errors were encountered: