-
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
Non-nullable type annotation syntax #27231
Comments
Just curious, why was '?' chosen? It seems very counter-intuitive to me, since a question mark implies some kind of optionality. In this case, we rather want to express a restriction. A more assertive exclamation mark would be more appropriate, in my opinion, but I did not study possible grammar conflicts, if that is the reason of this choice. |
The question mark means that the type is nullable/optionally null/union of type and Null, and its absence means that the type is just itself. |
Any chance union types will come along with this? They seem to be natural fits. |
This seems just an experiment for now. But I'd like to note that there might be needs for addition of APIs to the standard library. For instance, Kotlin has |
@lrhn Oh, I see. The title is misleading and the text does not clarify. It should actually be "Nullable type annotation syntax" instead of "Non-nullable type annotation syntax". |
@tantumizer
This is shorter return x ?? (throw new NullPointerException); See also #24892 I find |
Good questions, some not decided yet. Optional parameters: If nothing else changes, you'll have to make them nullable by adding a For a nullable Too many question-marks. Likely true, but they are consistently about A |
If you have
then |
As a clarification: if we add only If we don't allow downcast assignments, then there must be a way to go from nullable to non-nullable.
@tatumizer: I'm definitely interested in the reasons for why you had to use |
Currently the nullability experiment is about syntax only. In Patrice Chalin's proposal ( When a type variable can stand for a nullable as well as a non-null type it Because of complexities like this, there are quite a number of issues that On Tue, Sep 6, 2016 at 3:14 AM, Tatumizer [email protected] wrote:
Erik Ernst - Google Danmark ApS |
Replying to a few random things that weren't already covered:
It's the same syntax used to represent nullable types in C#, Ceylon, Fantom, Kotlin, and Swift.
We are interested in exploring union types too, but they're a big feature with a lot of consequences, so we aren't working on them right now. There's only so many hours in the day. :)
I considered that, but the obvious response is that Dart 1.0 already has nullable type annotations—all type annotations are nullable. So this is really about adding a way to express non-nullable types. And the way we do that is by adding new syntax for nullable types and changing the existing syntax to mean non-nullable.
Me too, but something along these lines might be worth doing. A big part of why we are doing an experiment around non-nullable types is to get answers to usability questions like this. How often do users need to assert that they know something isn't null when the compiler doesn't? We're hoping to implement enough of the static checking to be able to answer that confidently. String foo = map["foo"]!!; // I'm sure it's not null, I've just assigned it! Another part of this experiment is determining how we need to change our core libraries to make it pleasant to work with non-nullable code. In this case, I think Map should support two accessor methods. One returns There's some interesting API design questions about which of those operations should be
That entire class was designed around the idea that It's also probably true that core low-level collection classes like this will bear the brunt of the manual null checking. They are closer to the metal and need to do things a little more manually. Higher-level application code should hopefully be able to use non-nullable types more easily.
I have an answer in mind for this, which I think lines up with Patrice's proposal, but I haven't verified that or written mine down in detail yet. (That's why this issue is about non-nullable syntax. :) ). The short answer is that here, since you have no constraint, E can be instantiated with either a nullable or non-nullable type. If E was constrained to some non-nullable type, it could only be instantiated with a non-nullable type.
If you want to say E is some specific type, you can give it a constraint and that implicitly constrains it to be non-nullable too, unless the constraint is nullable: class Point<T extends num> {
T x, y; // <-- These are non-nullable.
Point(this.x, this.y);
}
new Point<int>(); // Fine.
new Point<int?>(); // Error! Constraint is non-nullable.
class Pointish<T extends num?> {
T x, y; // <-- These may or may not be nullable.
Pointish(this.x, this.y);
}
new Pointish<int>(); // Fine.
new Pointish<int?>(); // Also fine. I don't currently plan to support a constraint that says, "The type must be non-nullable, but I don't care anything else about the type." I could be wrong, but it doesn't seem very useful to me. |
With the approach where You could invent an operator computing a "type difference" (think set difference), say |
No, the collections don't have constraints and should accept nullable type arguments. My point was that the implementation of LinkedList takes for granted a type system where E is always nullable and non-nullability is not expressible. Once that assumption is no longer true, the implementer of LinkedList, might change how it's implemented. Maybe something like: class LinkedList<E extends LinkedListEntry<E>>
extends Iterable<E> {
E? _first;
E get first {
if (_first != null) return _first as E;
throw new StateError('No such element');
}
E get last {
return first._previous;
}
// ...
}
Yeah, you may be right, though I'd like to see how it actually works out in practice in the context of Dart and it's core libraries. |
Currently, it would need to be There are other similar cases where the null initialization would be annoying: int x;
if (someBool) {
x = 499;
} else {
x = 42;
} If these cases are too common we will investigate definite-assignment strategies, like in Java, and see if they would fix that problem. |
We could do that, but I think it's probably better to be explicit here at the expense of a little verbosity. I think it would be a bad thing if adding an initializer to a variable changed how its type annotation was interpreted.
The last time I poked around a corpus, I found that ~90% of variables were non-nullable, so I don't think the |
@tatumizer I worked several years in a language that didn't have I liked it a lot to not have to deal with I think this is the main point of A way would be (I think it was mentioned somewhere above) to define default values. int index; could be treated like int index = 0; This would be similar to now where it is treated like int index = null; For |
Why do you think that?
I think if there is not a defined default value (like |
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 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. |
Yup, that's fine.
Well, in this case, the answer would be just 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...
} |
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 class NumSet<T extends num?> { ... } This means that in the body of NumSet, |
The other two would be: class Sorter<T extends Ordered<T>?> {}
class Sorter<T extends Ordered<T?>> {}
class Sorter<T extends Ordered<T?>?> {} The |
class MyGenericClass<T extends Object> {...}
class MyGenericClass<T extends Object?> {...} These would be equivalent since Null is a subtype of Object.
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 |
@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 dart-lang/language#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 dart-lang/language#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
The text was updated successfully, but these errors were encountered: