Closed munificent closed 5 years ago
The very fact that you had to introduce so many syntax rules to cover nullables is the admission that nullable types are not really types.
No, all it shows is that before now, Dart had no syntactic distinction between types and classes. There are places in the language where you must refer to a class. When you're specifying a superclass, superinterface, etc., you need to refer to some class declaration.
Before this proposal, the syntax for types was indistinguishable from the syntax for referring to a class, because there were no type notations that didn't look like classes. As soon as you add ?
for non-nullable types, explicit function type syntax, union types, tuples, or any of the other panoply of possible non-nominal kinds of types, we would end up having to split the grammar in this way.
It's just that up to now, we didn't need to, so we could have type in the grammar do double duty as a type and a reference to a class.
@eernstg @munificent: no worries; the original proposal is a bit lengthy, but nullity (as we all know) can be tricky to do "right" in the context of Dart.
All: thanks for keeping the discussions moving forward.
@munificent : is this a valid syntax:
var map=new Map<String?, String>();
Yup, that's fine.
Assuming the answer is "yes": suppose I want to implement my own map that only works with String keys, but I want null to be a valid key. How to write definition of this generic class:
class StringMap<K extends WHAT???, V> {...}
Well, in this case, the answer would be just class StringMap<V> { ... }
. String is a sealed type, so there's no reason to make it generic on the key type. The only valid type argument would be String. :)
But let's say you want to define a number set that can be used with ints or doubles and you also want null to be a valid member. You would do:
class NumSet<T extends num> {
void add(T? value) { ... }
void remove(T? value) { ... }
// etc...
}
I made it generic precisely to show that it can handle null keys - so that it can be instantiated with both String and String?
Ah, sorry. I thought you meant you wanted it to always support null keys, regardless of the type parameter type. In my NumSet example, you can not do NumSet<int?>
because the constraint is num
, which is non-nullable. If you want to allow that, you'd do:
class NumSet<T extends num?> { ... }
This means that in the body of NumSet, T
is now a nullable num
type, so before you can call methods on it, you have to test for null first.
But why?
The other two would be:
class Sorter<T extends Ordered<T>?> {}
class Sorter<T extends Ordered<T?>> {}
class Sorter<T extends Ordered<T?>?> {}
The ?
was in the wrong place. (Also, I think when I first commented I may have forgotten that our tentative plan does allow ?
in constraints.)
class MyGenericClass<T extends Object> {...}
class MyGenericClass<T extends Object?> {...}
These would be equivalent since Null is a subtype of Object. Null|Object
collapses to Object
.
(The latter can be instantiated with any concrete type, but the former - only with non-nullable concrete types)
My current idea for the semantics does not give you a way to express "any type, but not nullable". If the only thing you know about the type parameter is that it's Object, it's not the end of the world to permit null—it supports all of the methods that Object does. It is an object.
All of these questions would be answered by a proposal for the semantics, which I have not yet written down. This issue is just for the syntax.
Do you think we can table this discussion until I have a real proposal to go on? Right now, we're sort of doing a breadth-first traversal through the semantics one comment at a time, which isn't an efficient use of either of our time.
If we wish to support a distinction between Object
with and without null
we can remodel the type hierarchy such that Null
is not a subtype of Object
any more, but we haven't decided that this is a good idea.
@munificent the link to the dart2js bug is wrong up top. Off by one error 😉
Is this not happening? :/ I find this to be a really useful concept for ensuring type safety of my code, and along with the lack of union types is making me wary of switching from TypeScript/React Native to Dart/Flutter in future projects.
It's not happening yet. Moving Dart to strong mode is a necessary pre-condition for non-nullable types. We are doing that in Dart 2. We hoped to get non-nullable types in at the same time, but it proved to be too big of a change to fit into 2.0, so it's going to have to wait until a later version.
So now as Dart 2.0 is released would be there any changes so it finally get happened?
Now all that's left is to design and implement non-nullable types, figure out a migration plan, add language features to make them more usable, etc. :) Basically, we have to do all the work. It's a giant feature.
If this year there was only one feature that dart shipped, this would be my choice. I really hope it becomes a high priority soon.
@gbaldeck and that's exactly what is happening this year see https://github.com/dart-lang/language/issues/110 :)
@munificent Should we close this and all associated issues and indicate that people can follow along the new language process in the language repo?
Yeah, good call. Closing this in favor of https://github.com/dart-lang/language/issues/110 which is the main tracking issue for the current plan to add non-nullable types.
This is the tracking bug for the real implementation of the postfix
?
syntax to mark a type annotation as nullable as part of supporting non-nullable types (#28619). See the "Syntax" section of the proposal for details.Since we're still only in the prototype phase, consider this bug to be on hold. If we decide to go ahead and ship a real implementation, I'll update this.
Flag
While this is being implemented, to ensure we don't expose users to inconsistency in our tools, it should be put behind a flag named
nnbd
.Tracking issues