Feedback on Null-Restricted and Nullable Types

Remi Forax forax at univ-mlv.fr
Wed Sep 11 08:03:26 UTC 2024


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Olexandr Rotan" <rotanolexandr842 at gmail.com>
> Cc: "Enrique" <enrique.arizonbenito at gmail.com>, "valhalla-dev"
> <valhalla-dev at openjdk.org>
> Sent: Wednesday, September 11, 2024 2:27:53 AM
> Subject: Re: Feedback on Null-Restricted and Nullable Types

>> On Sep 10, 2024, at 5:43 PM, Olexandr Rotan < [
>> mailto:rotanolexandr842 at gmail.com | rotanolexandr842 at gmail.com ] > wrote:

>> I would argue that not the syntax around nulls should be hideous, but rather
>> non-null should somehow be the default option

> We’re going to get this suggestion a lot. It is pretty clear that “non-null” is
> a better default interpretation of an otherwise unannotated type name than
> “nullable” is; this results in a lower annotation burden for annotating old
> code, less visual noise for reading fully annotated code, a safer default, etc.
> And if we were designing a language from scratch, it would be pretty hard to
> argue against this.

> But as you acknowledge, there are billions of lines of Java code out there, and
> retroactively changing their meaning is a non-starter. People will surely
> suggest things like “what about a compiler flag” (please don’t), but that’s
> just forking the language and pretending we aren’t. And all the “obvious”
> suggestions have one or both of these characteristics.

> So, we truly understand how this would be yet another “Java got the defaults
> wrong”. And maybe there is a path — in the not-so-immediate-future — to opting
> into stricter null checking in a way that does not feel like a fork. And we’re
> going to think about that, and if we find something that seems acceptable,
> we’ll consider it. But as James says, if you don’t know the right thing to do,
> then the right thing to do right now is to do nothing. So we will not be
> tackling the “wrong default” problem at the same time as we are addressing null
> restriction. (Besides, there’s enough impossible problems in Valhalla that we
> don’t need to add another one onto our plate.)

I see non-null by default as a side effect and not as a goal. 
The goal is to simplify the model we expose to users. 
It's clear that the underlying model is a 3 states model (let's pretend there is no type variable for a moment), so String, String? and String!. 
The main advantage of saying it's non-null by default is that it simplify the user model to a 2 states model, either String or String? so the user model is easier to understand. 
Obviously, once the code interacts with methods from the old world, the raw Strings are still there lurking but having a local 2 states model is i believe still a win. 

So the feature is providing a local 2 states user model and the nice side effect of that is having non-null by default. 

There are drawbacks to the local 2 states user model, on top of my head, how to opt-in ? Is making type variables a special case a good idea ?, should the bounds of a type variable be non-null too ? Is having no way to represent raw nullability not a problem when trying to guide inference ? And i'm sure many more. 

Rémi 

>> , so people will not bother adding anything on top of that unless explicitly
>> needed. I'm not really sure if it's achievable with preserving source backward
>> compatibility though, given that currently, default is unspecified nullability.
>> I think that many people will bother adding ! as much as ?, honestly. Most of
>> the production code I have worked with didnt even care about returning an
>> unmodifiable view of collection instead of the real one or wrapping nullable
>> value in optional in getters and hiding no-args constructors in POJO using
>> protected keyword. Honestly, the sad truth (in my experience) is the fact that
>> if something is not done by default, it will not be done manually in the vast
>> majority of cases, no matter how potentially beneficial it is. I would say that
>> best option would be to introduce compiler flag that enables non-null by
>> default, and eventually, after most users are migrated, make it true by
>> default.

>> Regarding new null in language. I (unfortunately) spend at least half of my
>> working with typescript. To say that this null-undefined fuss is terrible is to
>> keep silent. That's just a complete mess. Question mark in type stands only for
>> type | undefined, want to accept null - go and do var?: type | null - just
>> terrible. Question mark notation in field access chains is also so strange: it,
>> contrary to ? in type, accepts both null and undefined, and it often leads to
>> confusing situations when you expect one of those two, but get another, logic
>> in ?? still executes, and your erroneous state passes down through long chain
>> of invocation until it finally fails somewhere and good luck debugging it/ Two
>> different nulls is just a horrible idea in so many ways.

>> PS: Nothing bad with nulls by themself. Absence of value is also a value in many
>> scenarios

>> On Tue, Sep 10, 2024 at 8:46 PM Brian Goetz < [ mailto:brian.goetz at oracle.com |
>> brian.goetz at oracle.com ] > wrote:

>>> The following was received on valhalla-spec-comments.

>>> Point #1 rests on an assumption that the goal of this feature is to eliminate,
>>> or at least strongly discourage, nulls. This is a common assumption — many
>>> people are so-called “null haters” — but encouraging or rewarding such biases
>>> is not the role of the langauge. Nulls are not intrinsically bad; the problem
>>> with nulls is that they are so often unexpected, and so people code as if they
>>> would never happen. Reflecting nullity in the type system allows you to reason
>>> about when they are expected, and make reasoned choices about where nulls are
>>> appropriate or not.

>>> You state your position pretty explicitly here:

>>> >> The syntax to use null must be "disgusting enough" in order to
>>> >> discourage people from using it.

>>> I’m not going to criticize your opinions about nulls, but it is also OK to have
>>> different opinions about nulls, and it’s not the role of the language to insist
>>> that one or the other is the only valid set of feelings about it.

>>> Your second point is what we call “inventing a new null”; it is a tempting
>>> answer, but is overwhelmingly likely to just make the problem worse, because
>>> now there are two ill-behaved states.

>>>> On Sep 10, 2024, at 10:46 AM, Enrique < [ mailto:enrique.arizonbenito at gmail.com
>>> > | enrique.arizonbenito at gmail.com ] > wrote:

>>> > REF: [ https://openjdk.org/jeps/8303099 |
>>> https://openjdk.org/jeps/8303099 ]

>>> > Feedback 1:

>>> > Using the syntax "Foo?" for nullables values does not discourage the
>>> > use of nulls in code.

>>> > In my experience working with Kotlin, novice (and not so novice)
>>> > developers just find themselves comfortable enough adding an extra
>>> > character and continue to infest code with nulls.

>>> > The syntax to use null must be "disgusting enough" in order to
>>> > discourage people from using it.

>>> > For example something like Foo!!!NullBackwardCompliant!!!. This syntax
>>> > is ugly enough to scare people about using it.


>>> > Feedback 2:
>>> > Most people naively tends to use null in next scenarios:

>>> > * A class/record must be initialized, but due to the data flow, some
>>> > of its members are not yet known in "present time". They depend on
>>> > future input data/processing.
>>> > * Some member must not take any value in a given context.

>>> > Ideally the JVM would be extended to support "FUTURE" and "NA" (Not
>>> > Apply) values in parallel to null. Any read must throw a
>>> > FuturePointerException or NAPointerException.
>>> > While the behaviour looks similar to the current null and
>>> > NullPointerException, it makes error debugging much easier to
>>> > understand:
>>> > * A FuturePointerException promptly warns about some race condition:
>>> > Some code tried to read a value before some process (maybe another
>>> > thread) initialized it.
>>> > * A NAPointerException promptly warns about some error in the business
>>> > logic. Some code didn't contemplate the NA scenario.

>>> > "catch" blocks will also be able to make much more sensible decisions
>>> > (when compared to the pervert NullPointerException).

>>> > Without any JVM extension, a compiler most probably can automate a
>>> > similar behavior with a syntax similar to:

>>> > var Foo1 = Future
>>> > var Foo2 = NA

>>> > Foo1 and Foo2 are **NOT** nullables.

>>> > Foo2 becomes a constant (It will never change once defined and the
>>> > compiler can make many verifications out of the box).

>>> > Foo1 must never be read until a given value is provided (after which
>>> > it will probably become constant).


>>> > My two cents!

>>> > Enrique Arizón Benito
>>> > Software Gardener
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240911/e4b538bd/attachment.htm>


More information about the valhalla-dev mailing list