-
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
Support non-nullable types. #22
Comments
This comment was originally written by [email protected] Issue #24 has been merged into this issue. |
This comment was originally written by [email protected] some input from the author of null references: http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare |
This comment was originally written by [email protected] I have to agree. From reading through the tutorial, it sounds like Dart is handling null even worse than most languages. (Even numbers might be null?!) Given that types are optional, it would make sense to allow null when the type is unspecified (var or dynamic return), but if the user (programmer) is explicitly saying that this variable is an int, why should the type checker assume that null is ok? Null can be useful, but if I am choosing to explicitly declare a type, please let me also explicitly say whether it is allowed to be null. I personally would prefer option types, but given that this language seems to be trying to be similar to C++/Java/Python, perhaps declaring things nullable would be better. For example, if you write "nullable int n = 42", then "n = null" would be allowed. Another option would be to explicitly annotate variables as not null, like "not_null int n = 42", but IMHO, the only advantage to that would be that it wouldn't break existing code, and given that this is a new language, that's not a very good reason. http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake |
This comment was originally written by [email protected] I don't think it is possible to completely forbid null-references in a language that allows dynamic types. However, on page 73 of the Dart language specification draft, the question is asked "Should we do something with respect to non-nullable types?" and my answer is YES, PLEASE, I'M BEGGING YOU. |
This comment was originally written by [email protected] There was discussion of allowing int? foo to mean that foo might be nullable, and just int foo would mean it wasn't. However, there were some complications, particularly with the idea of types being optional -- ie, removing a non-nullable type annotation could change the behavior of the program. |
This comment was originally written by [email protected] May I ask what these complications look like? |
This comment was originally written by [email protected] I would suggest to borrow C# concept which separate value type from object and only object can be null. If we want value type (like int) be null we can use a constructor and wrap it as a object nullable<int>. A plain int can be null is usually a disaster for dev.. |
This comment was originally written by [email protected] Nullable types should definitely be added. At least in the expected form where checker would warn you while prod env would let you assign nulls anyway. |
This comment was originally written by [email protected] One example of changing behavior: int foo = null; // throws NPE If you strip off the types, you change the behavior of the program. |
This comment was originally written by [email protected] Nullable types need to exist as long as default constraints are identified for the type. Much like C# default(t) with a user pragma like declaration . This would allow for ease of intrinsic types and nullable types based on conditions. |
This comment was originally written by [email protected] @JaT I don't understand. A type warning would be perfectly adequate in this case, and not change the behavior of the program if the type is removed. Or am I missing something? |
This comment was originally written by [email protected] So what happens when you dereference a null pointer (that was declared to be non-nullable) at runtime? I don't see how you usefully continue -- the program is broken, and you are better off failing at the point you assign the null pointer to it. You can do things like require such variables to be initialized and forbid assignment of a null literal, but in general you are not going to be able to tell if the execution of the program would lead to assigning a null to a variable -- it may even reference external things, such as a server or the DOM, that the compiler can't know the behavior of. Even if you require that such functions be declared as "Foo?" and require explicit null checks when assigning to a "Foo", the server might still return a null when it was declared not to. |
Removed Type-Defect label. |
This comment was originally written by [email protected] Added Triaged label. |
This comment was originally written by [email protected] jat: I thought I read that when running in production mode any type errors were supposed to be ignored, so types don't affect the behavior of the program. When running with checking enabled they do, but that is regardless of whether you allow non-nullable types. For example: The same applies to your argument wrt a server or the DOM. If it can return a null when it was declared not to, couldn't it just as easily return a string when it was declared not to? If you are really worried that making types non-nullable by default would result in question marks all over the place, could you at least support explicitly marking a variable as non-nullable? That would be completely backwards compatible, but having it sooner would mean library writers would be more likely to use it, which would benefit everyone. I still think explicitly marking nullable ones is better, because that is when you should be thinking about the fact that it might be null, and the presence of a marker tends to be a better reminder for people than it's absence. |
This comment was originally written by [email protected] Agreed. For a given type T, a variable declared to have type "T" should not be allowed to be null, whereas a variable declared to have type "T?" should be allowed to be null. I don't see why changing the declaration is not allowed to change the behavior of the program, though. Changing a function from allowing null to not allowing null is always a backwards-incompatible change, and I think -- in a lot of the use cases -- the function would not have actually supported null, to begin with. The use of the annotation just makes this lack of support more well documented and exposes the error sooner. |
Well, that is sort of the fundamental idea behind optional types: they're optional. You're of course free to disagree with that concept, but that probably means no amount of feature changes will make Dart into the kind of language you want.
Changing the body of a function to something that no longer will work if you pass null is definitely a backwards-incompatible change. That's different, though, than changing the type annotation of what the function expects.
Right, that's what's great about type annotations (or annotations in general): they give a nice user-visible distillation of what the API expects. But you can get that benefit without the type annotation enforcing what it communicates. That's the model of optional types: it will try to tell you that it thinks you're doing something wrong, but it won't totally prevent you from doing it. You're presumed to be smarter than the type checker, so in the case of a disagreement, it will submit to your will. |
This comment was originally written by [email protected] Ok, I'm afraid I just don't get it. My understanding of optional types is that you don't need to declare the types, but you can if you want to. However, where does it say that declaring the types has no effects? If declaration types have no effects, then what the hell is the point in adding them back? Isn't enforcing type-safety (which would include nullability), a motivation for developers to add back types? If types have no purpose other than documentation, what's the point? |
Gilad can explain it better than I can: http://www.dartlang.org/articles/optional-types/
They do a few things for you:
I think those cover most of what you want from types (performance is the other piece, but I think Dart can be plenty fast without that), but note that none of them affect the runtime behavior in unchecked mode. So you can get what you want from types, but you aren't required to satisfy the type system in order to run your code. |
FYI a relevant DEP is dart-archive/dart_enhancement_proposals#30 |
Here's a minimal repro that this CL fixes: `ui.dart` ```dart library dart.ui; import 'dart:ffi'; part 'foo.dart'; ``` `foo.dart` ```dart part of dart.ui; @Native<Void Function()>(symbol: 'foo_func', isLeaf: true) external void foo_func(); ``` When compiling with `compile_platform.dart` with `--target=dart2wasm`, the following error appears: ``` Unhandled exception: Verification error: Target=wasm, VerificationStage.afterModularTransformations: Invalid location with target 'wasm' on FunctionNode() (FunctionNode): RangeError (offset): Invalid value: Not in inclusive range 0..56: 91 Context: 'foo_func_$import'. Node: 'FunctionNode()'. #0 VerificationErrorListener.reportError (package:kernel/verifier.dart:81:5) #1 VerifyingVisitor.problem (package:kernel/verifier.dart:222:14) #2 VerifyingVisitor._getLocation (package:kernel/verifier.dart:1361:7) #3 VerifyingVisitor._hasLocation (package:kernel/verifier.dart:1370:26) #4 VerifyingVisitor.getSameLibraryLastSeenTreeNode (package:kernel/verifier.dart:1342:28) #5 VerifyingVisitor.localContext (package:kernel/verifier.dart:1382:24) #6 VerifyingVisitor.defaultDartType (package:kernel/verifier.dart:1491:41) #7 Visitor.visitVoidType (package:kernel/visitor.dart:1309:37) #8 VoidType.accept (package:kernel/ast.dart:11190:42) #9 FunctionNode.visitChildren (package:kernel/ast.dart:3919:16) #10 VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10) #11 VerifyingVisitor.visitWithLocalScope (package:kernel/verifier.dart:266:5) #12 VerifyingVisitor.visitFunctionNode (package:kernel/verifier.dart:721:5) #13 FunctionNode.accept (package:kernel/ast.dart:3908:38) #14 VerifyingVisitor.visitProcedure (package:kernel/verifier.dart:620:19) #15 Procedure.accept (package:kernel/ast.dart:3311:40) #16 visitList (package:kernel/ast.dart:14488:14) #17 Library.visitChildren (package:kernel/ast.dart:591:5) #18 VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10) #19 VerifyingVisitor.defaultTreeNode (package:kernel/verifier.dart:196:5) #20 TreeVisitor.visitLibrary (package:kernel/visitor.dart:503:35) #21 VerifyingVisitor.visitLibrary (package:kernel/verifier.dart:367:11) #22 Library.accept (package:kernel/ast.dart:577:38) #23 visitList (package:kernel/ast.dart:14488:14) #24 Component.visitChildren (package:kernel/ast.dart:14320:5) #25 VerifyingVisitor.visitChildren (package:kernel/verifier.dart:259:10) #26 VerifyingVisitor.visitComponent (package:kernel/verifier.dart:342:7) #27 Component.accept (package:kernel/ast.dart:14313:38) #28 VerifyingVisitor.check (package:kernel/verifier.dart:171:15) #29 verifyComponent (package:kernel/verifier.dart:69:20) ... ``` The issue seems to be that after doing this native transformation, the node's `fileUri` references the enclosing library (`ui.dart` above), but the `node.location` references the actual source file (`foo.dart` above) indirectly through `node.fileOffset`. This ends up being an issue when compiling the platform dill in Google3, but I didn't look into why `flutter build web --wasm` isn't broken. Internal bug: b/292172146 Change-Id: I2b8d7d215b2c36354860257ce651d50168e9523d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/315360 Reviewed-by: Ömer Ağacan <[email protected]> Commit-Queue: Jia Hao Goh <[email protected]>
Admin comment: This is activaly being worked on, see dart-lang/language#110 and https://dart.dev/null-safety
This issue was originally filed by [email protected]
Short version: Null pointers are a really good way to mess up a program at runtime, and I'd like the Dart team to reevaluate whether they're absolutely required.
Slightly longer version: I would say the #1 cause of issues in my programs (excluding logical errors/requirements errors) are NPEs. Having a language support NPE removal, be it via some clever compiler warning or simply removing null altogether, would be wonderful. I'm personally partial to Scala's method of null removal, but I'm sure PL gurus like yourselves have seen many others.
Dart has a stated goal of avoiding the creation of programs that "are difficult to debug or maintain." NPEs are a huge pain point in this regard. I'd be really happy if the Dart team reevaluated whether they are absolutely required to achieve the other aims.
The text was updated successfully, but these errors were encountered: