-
Notifications
You must be signed in to change notification settings - Fork 208
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
NNBD List constructor is confusing. Consider removing it entirely. #746
Comments
With this proposal, in NNBD code, The signature Only allowing nullable type parameters is, as you say, not supported by the language, and it has to work with type variables too. I'm also not just worrying about migration, but also about the API that we end up with. The |
I think removing the constructor entirely is likely to be annoyingly painful for relatively little benefit. How about we just mark it deprecated and have the error message point people to list literals, (For what it's worth, "Effective Dart" has long pushed users towards list literals and a significant fraction of calls to |
Remind me why we made this choice? Why are we not migrating this constructor to |
This is what is currently planned right? What do you mean by "There's no good way to do so"? |
This is probably a bad idea. It sounds like a recipe for runtime casts failures and/or static errors to me - most of the time you probably want to end up with a |
This feel odd to me. The existing functionality is unique, and useful (albeit only in relatively rare situations). This change make the functionality redundant with |
I think a secondary issue is what developers have used the unnamed List constructor for. In the past, I've used the unnamed constructor to preallocate the size of a growable array: This is because Dart's List lacks what other language's lists/vectors have: capacity. Capacity is an implementation detail. Capacity exists because there are times where we know better than the VM, we know our final list size, and we don't want to pay the cost of growing the list in the application's common case, just in the extreme case. Post-NNBD, Dart has no way of maintaining this behavior without making the entire list nullable. The lack of expressive List types have made juggling the behaviors of various types of lists hard. We have lists that act like lists, we have lists that act like arrays, we have lists that are immutable, etc. It may be too late for a |
In addition to that @tatumizer, typed arrays (which are Lists!) are initialized to 0 and do not allow null. They do not allow for growing, but can technically shrink with typed array views. These are 1:1 with Java's primitive arrays. You cannot resize them, you can (unlike Java) make sub-views. Question for @lrhn: If you have a constructor like Implementation might be a bit weird, but ultimately having the ability to get the default zero value for a type parameter would be useful. It wouldn't even need to be exposed directly, just indirectly when you omit the default value for a parameterized type (so devs could write When the given type is statically known to not have a default initializer, the parameter (and transitively all preceding parameters) becomes required. If the type is not statically known to have a default initializer, a runtime failure should occur ( To generalize default initialization, you could say something like "the default value for a union type is the first type with a default value, ignoring any preceding types without default values". Then you could declare that |
tl;dr: I think the currently specified NNBD behavior for the My underlying motivation here is to end up with a good API in the long run, even if it costs a little more in the migration period. And yes, we statically disallow creating a list with a potentially nullable type. When I say that there is "no good way to do that", I mean that the currently specified behavior isn't that good. It's not founded in the type system, and it's an exception only for this one constructor, and it's an exception we'll have to keep around forever. The current special-casing of the What I would create from scratch is something like It's is redundant with
No, it does not. The declaration is invalid because the optional parameter has a type which is potentially non-nullable and it has no default value. We have made some migrations where an optional parameter gets type |
Maybe we can. We still need to make |
I think the current implementations already use magic here, since |
I think I might be more supportive of this approach. We say:
This doesn't seem substantially more breaking to me (the migration tool should just rewrite the constructor to |
Deprecation plus disallow in NNBD mode should make the constructor eventually go away. |
I have specified that it is an error to use this in Null safe code. |
tl;dr: Proposal
I propose that we:
List
constructor toList([int? count, T? value = null])
, but statically treat it asList(int count, T value)
in NNBD code, and asList([int count])
in non-NNBD code (like now).List<T>()
to<T>[]
.List<T>(n)
toList<T?>(n, null)
. (If the code isList<T>(x.length)..setAll(0, x)
or similar, perhaps recommend to the user to rewrite using a literal with a spread).Background
The NNBD behavior of the unnamed
List
constructor is that:List<T>()
creates an empty growable list, and is redundant with the[]
literal.List<T>(n)
is invalid ifT
is potentially non-nullable (even ifn
is zero, because the compiler doesn't check the value, just whether there is syntactically an argument).List<T>(null)
is still possible and a run-time error. It differs fromList<T>()
.See also #509.
The second item means that you can never create a
List<T>(n)
whereT
is a type variable.This has lead to some amount of confusion during migration of existing code. Feedback is that it is confusing, and that, e.g., they expect
List<T>(0)
to work.I recommend that we reconsider this design (which was never great) and either:
List.filled
/List.empty
.List.filled
. ThenList<int>(5, 0)
works. It's redundant withList.filled
, but shorter and if we make the second argument required, it will prevent the errors fromList.filled
when the fill value isnull
. OrWe'll have to include this in the migration tool, but I think it might be worth it. The
List()
is useless, theList(n)
is dangerous and doesn't cover the uses that users actually care about.The current behavior is inconsistent and confusing. There has always been special-casing around the
List
constructor, and we are already planning to special behavior for it in NNBD code. I think this change is a cleaner approach than just sticking to the current behavior.The text was updated successfully, but these errors were encountered: