-
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
Allow syntax for classes that implement all members (regression from Dart1) #32360
Comments
As far as I can tell, implementing this is trivial -- just silence warnings of the form |
What do you mean by this? There should be no explicit casts at all, if you're using dynamic. If you're trying to cast to specific interfaces, that would never have worked in Dart 1 either (at least not checked mode). Just marking something with |
Following up on the feature request, let me start by saying that I really do think that If you want to mock a specific interface (or set of compatible interfaces even), you can do so just by using no such method and specifying what methods you want to implement. You will also get the benefit that they will be assignable to the types you're mocking! If you want to mock all interfaces dynamically (I encourage you not to, see above, but your business) then dynamic works as well as The only use case we've lost is the ability to mock specific interfaces statically, plus everything else in the universe dynamically, using a single class. This seems.. niche. Do you have good examples of why this is important? (Seriously, I want to understand the use cases here). I do think that we should have a good way to handle more general mocking, and I would like it to be lightweight and easy enough to use for quick hacks. But I really do feel that we're better off without a primrose path feature like cc @lrhn |
The effect of having There is indeed no type safety if you do that, you can't be sure that the object you are using is actually an instance of I do see a use for that feature, even if it is a highly dynamic feature - it's just slightly less dynamic than actual |
tl;dr @Hixie, I'd like to make the point that the use of variables of type The situation in Dart 1 with That would mean "checking statically that invocations of members of main() {
dynamic<List<int>> xs = ...;
num n = xs.length; // OK, `List<int>` has a `length`, `int` is assignable to `num`.
xs.add(n); // OK, with downcast (so it's failing with `--no-implicit-casts`).
xs.whatEverYouWant(whatever: 42); // OK: invoking a "dynamic" member, no checks.
} The point is that this allows you to use A Dart 1 class So using the type |
Consider this code: What I would like it to look like is: Future<Calendar> _getCalendar() async {
final Json data = Json.parse(await _request('get', '/api/v2/event.json'));
try {
final Json values = data.event.asIterable().single;
if (values.status != 'ok')
throw const FormatException('status invalid');
if (values.total_count != values.events.asIterable().length)
throw const FormatException('total_count invalid');
return new Calendar(events: values.events.asIterable().map<Event>((Json value) {
return new Event(
id: value.id.toString(),
title: value.title.toString(),
official: value.official.toBoolean(),
description: value['description']?.toString(),
location: value.location.toString(),
startTime: DateTime.parse(value.start_time.toString()),
endTime: DateTime.parse(value.end_time.toString()),
);
}).toList());
} on FormatException {
return null;
} on NoSuchMethodError {
return null;
}
} In particular, note the removal of several casts, and the replacement of "dynamic" with "Json", allowing for much better type checking. (The Json class is implemented here: https://github.com/seamonkeysocial/cruisemonkey/blob/master/lib/src/json.dart)
I would be fine with having the |
In the case where abstract class Json {
operator[](String key) => /* TODO: Implement. */
} Then you'd write: try {
final Json values = data['event'].asIterable().single; ... instead of: try {
final Json values = data.event.asIterable().single; Same amount of "type safety" without any of the overhead of this feature. |
Not doing that is the entire point of the |
Do you really value Sorry just seems silly. |
Sure. It's the difference between Json code being barely readable and Json code being too ugly to want to use Json. :-) |
Personally |
You're just as likely to misspell |
I sympathize that you're personally unhappy, but I don't think this will change. Personally I think this bug should be closed as WAI, the Dart2 team spent a lot of time and effort deciding this already, removed it from the spec, updated the tools, etc. Perhaps better metaprogramming in Dart 2.1+ could help alleviate some of this pain, but until then |
This is what the code above would look like with square brackets. It becomes much harder to read, and additionally, it loses the key distinction between "this should be here and if it isn't it's an error in the data" and "this field is optional". Future<Calendar> _getCalendar() async {
final Json data = Json.parse(await _request('get', '/api/v2/event.json'));
try {
final Json values = data['event'].asIterable().single;
if (values['status'] != 'ok')
throw const FormatException('status invalid');
if (values['total_count'] != values.events.asIterable().length)
throw const FormatException('total_count invalid');
return new Calendar(events: values['events'].asIterable().map<Event>((Json value) {
return new Event(
id: value['id'].toString(),
title: value['title'].toString(),
official: value['official'].toBoolean(),
description: value['description']?.toString(),
location: value['location'].toString(),
startTime: DateTime.parse(value['start_time'].toString()),
endTime: DateTime.parse(value['end_time'].toString()),
);
}).toList());
} on FormatException {
return null;
} on NoSuchMethodError {
return null;
}
} This is a regression that makes existing code much uglier and fixing it would be simple. I don't really understand why we wouldn't want to fix it. Fixing this only requires two things: when a class is specially marked, have the analyzer act just like it does today if the static type is |
I think this is a place an in-person discussion with the team is going to be a lot of more productive (for everyone) than a GitHub issue. I'm happy to set one up if you'd like. |
Thanks for expanding on the use case, it's helpful. |
@Hixie I'm wondering why you use JSON directly at all. I always deserialize it to a class before I use it. This way I never have to use |
This is the code that deserialises it. |
The new semantics of As far as I know changing |
I don't think this should be closed. It's a valid request to not regress a feature that people have previously depended upon. As far as I can tell it would be simple to implement. It's essentially asking for a way to say you implement every method, getter, and setter. This is commonly implemented in many languages. |
I had a misunderstanding about |
I don't think it would be difficult to implement support for Given that it would enable usage of some code that used to have compile-time errors, I believe it is a non-breaking change. However, the decision to eliminate the special treatment of In order to reconsider the situation I think @Hixie's two code snippets are a good starting point for getting an idea about how much it matters: this example vs. this one. So we're basically discussing whether the avoidance of a lot of |
One small point, I don't believe this issue is (From a completely personal preference, I wouldn't want |
Yeah sorry if this wasn't clear earlier, this is definitely not something Flutter is trying to do. We try to avoid JSON in general. :-) |
This has now gotten even worse, because you can't implicitly cast from |
|
|
Ah, I think I know what may have happened. There was a regression, I think only in dev releases between 2.0.0 and 2.1.0, that caused
I take it you would like the no implicit cast option to still allow casts from dynamic? |
ah, interesting. I think it's fine to not have casts from dynamic in general (and require (Also, separately, I'd like |
Also see #35019 (sort of). |
It was previously possible to create a class that was typed, i.e. had known methods and such that the analyzer could type-check, while still allowing any unknown methods to be accessed, as in:
This no longer works (and there's no equivalent). This breaks existing code. The nearest equivalent workaround is to use
dynamic
everywhere, but that loses critical type checking and means the code has to be full of casts, which defeats the entire point of having a class like this (making the code succint and readable).cc @leafpetersen
The text was updated successfully, but these errors were encountered: