Feedback on Null-Restricted and Nullable Types

Olexandr Rotan rotanolexandr842 at gmail.com
Thu Sep 12 16:39:58 UTC 2024


Just an immediate thought about reading your mail. What is the difference
between the mutable safeval neverNullVar =
getFromNewFunctionWhoJustDoesntKnowThatNullNeverExistes() syntax and var
neverNullVar = getFromNewFunctionWhoJustDoesntKnowThatNullNeverExistes()!
(exclam on the end), which is really common in null-safe languages. I would
argue that if exclam syntax would be fail-fast, later is even more
plausible, especially considering the cost of adding new keywords to
language, even weak ones. Undefined nullability decorated with ! would just
become non-nullable.

The Future syntax somehow for me seems like a readonly (final in java) for
local variables. That would be nice to have, but If we go down to the level
of local variables, I would say that just a simple val keyword from kotlin
would be a much greater contribution to local variables immutability.

On Thu, Sep 12, 2024 at 7:20 PM Enrique <enrique.arizonbenito at gmail.com>
wrote:

> 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
> >>>
> >>
> >>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240912/df0d10e4/attachment-0001.htm>


More information about the valhalla-dev mailing list