There are certain situations in which it is helpful for debugging and maintainability to force NPEs to be thrown.
The suggestion is to add a new operator (.!) that will enforce that the object is not null before accessing it.
Its use would be as follows: var maybeNull:ValueHolder; maybeNull.value; // will return null maybeNull.!value; // will throw a NullPointerException
Comment #1
Posted on Sep 30, 2010 by Massive RhinoThis is a great feature. We often missed this while debugging our JavaFX script application. About the way to handle it, I'm wondering whether this wouldn't be easier to add execution profiles, a "debug" one in which all NPE are thrown (or at least logged) and a "production" one ignoring NPE?
Comment #2
Posted on Sep 30, 2010 by Quick PandaExpanding on the comment above, one of the things that might be useful to add to the language would be annotations. Annotations have many uses (easier unit testing, for a start), one of which could be to enable or suppress specific compiler behaviours. An @EnforceNPE annotation on a method, or even a block of code, could be used by the compiler to disable its default behaviour of suppressing NPEs. This behaviour could then be removed by merely deleting the annotation.
Another option would be to recognise a "debug" or "test" flag when compiling (as mentioned previously), and only have the @EnforceNPE annotations take effect when the code is build under that profile. The annotation could be combined with testing annotations, allowing NPE checks to be factored into unit tests and debug runs, while still suppressing them on release builds.
Comment #3
Posted on Oct 2, 2010 by Grumpy HorseI'm not a language expert but is it okay to copy-cat another language's style of handling NPE? or does a new language have to be original. Groovy has this facility and I'm sure most people may know how it looks.
Groovy: maybeNull?.value
(IMO) I think when it comes to familiarity developers tend to feel comfortable. I believe when C was popular most of us fell in love with Java. This is all so subjective of coarse.
Comment #4
Posted on Oct 3, 2010 by Helpful WombatBorrowing established patterns from other languages is always a good idea.
However, the Groovy syntax is a safe dereference, and I want the opposite semantics (null-checking dereference).
Although, in the spirit of borrowing syntax, maybe the exclamation mark should precede the dot... that binds it closer to the term that is being null checked: maybeNull!.value
Comment #5
Posted on Oct 3, 2010 by Grumpy HorseAh, I see what you mean by how it is the opposite semantics of Groovy's "?." (sorry misunderstood). The (null-checking dereference) feel seems interesting though. I do find that in Groovy I tend to pepper code with "?." I guess it's like a shorter version of an if condition in Groovy. Yes the (new way) seems like a good idea. Relating to what Simon said about annotations, that too seem like a nice feature. I wouldn't want people to abuse or use things in the wrong context causing the code to be hard to read.
Comment #6
Posted on Oct 3, 2010 by Helpful WombatThe debug/production and annotation ideas are good ones, but have some issues.
Personally, I like simple behavior where what you test behaves exactly the same as what you deploy. Any changes (no matter how innocuous) can cause issues that you miss while testing. For example, not throwing an NPE could actually result in a bug where code is catching exceptions, and the NPE changes the flow of control.
The annotations is more like a method or class-level switch. While annotations are nice, I prefer not to rely on them for language semantics. They are typically best reserved for uses where you cannot change the language (either as an API designer or for backwards compatibility reasons).
By the way, I added a separate enhancement to add annotations support to Visage.
Comment #7
Posted on Oct 5, 2010 by Swift BirdThis "anti-Elvis" operator may be indeed a good workaround, if everybody agrees that the default behavior of making nulls is preferable. But I wonder if we should keep the route of the original language plan; that route was improving the typesystem to better handle this. From Robert Fields comment on JFXC-3447: "It is high on my agenda to generalize cardinality, so that all types can be required or optional. This will move several things in the direction you desire. For one it will, internal to the compiler, give the information we need to reduce the number of null checks. It gives a possible way in for required values in an object literal."
I'm not sure if I completely understood that plan; but here is what I think. The existing language already tries to handle null, non-null and multiple-cardinality values (right now just sequences) in a unified way; so, sizeof nullRef = 0, sizeof notNullRef = 1, sizeof notNullSequenceRef = number of elements in the sequence. Another special case for nulls is insertion in sequences, and I think this is a very nice property of JavaFX Script sequences (but will comment on the separate issue for that).
The missing trick is, perhaps, enhancing the typesystem to assign static nullability types to variables. For example, I could have a function f(x: notnull String), where 'notnull' is either inference or explicit keyword. The null-handling of the parameter x could be performed at the call site, and not inside the function f(); so, if I try to invoke f(y), the compiler could have several interesting behaviors:
1) If y's nullability is undefined, perform a runtime null check at the call site, so if y == null the function cannot be invoked. 2) If y's nullability is null, same bejavior but without the overhead of runtime check, don't invoke the function. 3) If y's nullability is notnull, invoke the function.
In the first two cases, we can decide to either produce a compile-time error in both cases, or produce a runtime exception in (1) and compile-time error in (2), or not produce any error and just avoid invoking the function (if that function has a return type, just replace it with the null/zero/empty value for that type at the call site). The latter option is preferable because it's basically the behavior of the existing language, except that we are doing it eagerly, without a (possibly meaningless) half-execution of a function that contains code that needs a non-null reference at some point.
The "notnull" is a cheap syntax for this example, but we really need something smarter, perhaps similar to E/R models (0-1, 1, *) because the idea is to coalesce nullability and cardintnality in a single concept. A sequence-typed variable for example, will never be "null" because a null sequence reference is indistinguishable from an empty sequence: both contain zero elements and that's all!
(Notice that this design eliminates the anti-pattern of null arrays/collections in Java... many people follow a best-practice of never allowing null arrays/collections, using empty arrays/colections when necessary, in order to avoid NPEs or extra null-test guards everywhere.)
A final comment... we may need to wait until JDK 8 to ideally implement this idea, because it would benefit from JSR-308 (Type Annotations). Otherwise I don't see how we can encode extra typing information (nullability / cardinality) in the bytecode, to allow separate compilation and seamless integration with future Java stuff like the Checker Framework. So, maybe it's a good idea to put the entire discussion of nulls in the back burner.
Comment #8
Posted on Oct 6, 2010 by Happy MonkeyFWIW I prefer the ?. over .! or . with null handling. People usually will be coming from Java and will have embedded expectations on the behavior of ".". If the behavior is different in visage, it is better to use a deviating notation for the deviating behavior. Hence "?."
Comment #9
Posted on Oct 7, 2010 by Massive MonkeyEven as a Groovy developer, the ?. syntax still makes me think of the ?: ternary operation from C/C++, while the ! indicates the imperative action.
So to me, the .! syntax is a more logical way of saying: It is imperative for the execution of the program that this value exists.
While the ?. syntax implies to me: If this value doesn't exist then do something.
Status: Accepted
Labels:
Type-Enhancement
Priority-Medium