Feedback on Null-Restricted and Nullable Types

Enrique enrique.arizonbenito at gmail.com
Thu Sep 12 16:20:26 UTC 2024


Hi again,

just some extra thoughts based on previous comments.

Starting from the scratch, the simplest way to get rid of nulls would
be to remove it. This is completely possible as Vlang has proved.
https://vlang.io/
(As a curiosity I also proposed that same solution time ago in the LUA
list https://lua-users.org/lists/lua-l/2015-01/msg00222.html,
unfortunately I lack the time to implement it.)

Then the real problem is to keep compatibility with "the billion
dollar mistake". A "simple" way to do so would be:

Duplicate the syntax for declaration of variables.

We can use the current syntax with no change:

var maybeNullVar = getFromOldFunctionReturningNullsAtWill();
String maybeNull  = getFromAnotherOldFunctionReturningNullsAtWill();

then the new syntax is added:

mutable safeval neverNullVar =
getFromNewFunctionWhoJustDoesntKnowThatNullNeverExistes(); // ¹
safeString doesntApplyInContext = NA; // throw NAPointerException when
trying to read or update it. safeString is final by default.
safeString email = FUTURE; // throw FUTUREPointerException when trying
to read it, we can still update it "just once", then becomes
immutable.
mutable safeString stringState = FUTURE; throw FUTUREPointerException
when trying to read it, we can still update as many times as we want.

¹ Since we want to make things safe, let's just default to immutable
variables, and mark mutable explicitly.

To convert from unsafe to safe, a function will be needed:

final safeLong safeLong =
safetyFunctionTakingNonSafeAsInputAndReturningSafeAsOutput();

The "safe" prefix is a "30 seconds of thought", just to let it clear
to novices that the opposite is not safe and discouraged.

QA managers will have no trouble detecting code using unsafe vars. No
need for special tooling, just "grepping" the code.

Some future JDK "N" version can start warning about unsafe variables,
then N+20 version will force the use of a flag (or environment
variable) to compile unsafe code, finally N+100 will remove unsafe
vars from the language and JVM (I guess this will also boost the JIT
performance).

PS: I didn't know about the "null haters" term. I find it funny, and
definitely and considering that the null makes all of us waste time
and money, I invite everybody to join the "null haters" religion.
We can organize illegal parties and destroy null pointers to
celebrate, then escape before the police arrive.

Regards,

Enrique Arizón Benito
Software Gardener


On Thu, Sep 12, 2024 at 12:32 PM Olexandr Rotan
<rotanolexandr842 at gmail.com> wrote:
>
> I have given a lot of thought to this topic since my last mail here. Ultimately, what I have concluded on is that even if we try to somehow infer nullability, the limits of this inference are drawn on the bounds of the system and non-final fields. Nullability, though, can be inferred for local variables, and IDEs already are doing a pretty good job in this area, so I am not sure if this is a job of jdk to reinvent this analysis once again.
>
> So, the best idea that crossed my mind so far is to, instead of forbidding compilation by making non-null default, just be as annoying as possible *for unspecified nullability* (I emphasize that, as I see, problem is unspecified nullability rather then specified with '?'), by introducing some warning for any unspecified nullability type, at least at the bounds of type (i.e. public and arguably package-private members and their declaration members), but, if i were to decide, for all members of type. Local variables could have a bit more loose requirements imo, however, including them into inspection could encourage people to use var keywords if that was the point for any reason.
>
> I think that this annoyance is acceptable, because, as I see it, ? notation is essentially union of type and null, while ! is an explicit statement that this type does not contain null. Hence, warnings about unspecified nullability is just a warning that you don't give compiler enough information to validate your code, in some way like unchecked cast and raw types warnings currently. This kind of soft but steady power could potentially end us up in a situation when, eventually, there is little to none code without nullability notation.
>
> Also, as an option, a tool for nullability inference could be included in jdk as a separate tool instead of part of the compiler. This could smoothen the steep path developers would have to take o migrate to new java versions, by sparing them from work to manually add nullability info to each local variable
>
> On Wed, Sep 11, 2024 at 11:03 AM Remi Forax <forax at univ-mlv.fr> wrote:
>>
>>
>>
>> ________________________________
>>
>> 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 <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 <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 <enrique.arizonbenito at gmail.com> wrote:
>>> >
>>> > REF: 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
>>>
>>
>>


More information about the valhalla-dev mailing list