Nullable types and inference

forax at univ-mlv.fr forax at univ-mlv.fr
Mon Apr 29 22:01:18 UTC 2019


----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
> Envoyé: Lundi 29 Avril 2019 21:51:22
> Objet: Re: Nullable types and inference

>> The first case is a corner case and for generics, it works until someone try to
>> stuff null into a value type.
>> 
>> So instead introducing nullable value types in the language which make the
>> language far more complex than it should be, i think we should come up with a
>> far simpler proposal, to have a declaration site tagging of the method that
>> doesn't work with value types.
>> 
>> 
>>  // proposed syntax
>>  interface Map<K, V> {
>>    "this method doesn't work if V is a value type" public V get(Object o);
>>  }
> 
> We explored this idea in M3; we jokingly called this “#ifref”, which is to say,
> we would restrict members to the reference specialization.  This was our first
> attempt at dealing with this problem.  We gave up on this for a number of
> reasons, not least of which was that it really started to fall apart when you
> had more than one type variable.  But it was a hack, and only filtered out the
> otherwise-unavoidable NPEs.
> 
> More generally, there’s lots of generic code out here that assumes that null is
> a member of the value set of any type variable T, that you can stuff nulls in
> arrays of T, etc.  Allowing users to instantiate arbitrary generics with values
> and hope for no NPEs (or expect authors of all those libraries to audit and
> annotate their libraries) is not going to leave developers with a feeling of
> safety and stability.

To get a NPEs when you store something in an array of T, you need the array to be an array of inline type,
and currently it's not that easy because T is not reified.
There are two usual way to have reified array/data structure currently, either you send a Class value, like Collection.checkedCollection() and it's fine because the user is in control or you do something a little more fancy to transform the type signature emitted by javac to a series of Class, like Jackson/Spring does, and yes, these libraries will need to be fixed to choose the L-view of an inline class. 

> 
> Further, allowing users to instantiate an ArrayList<Point> — even when the
> author of ArrayList proposes up and down (including on behalf of all their
> subtypes!) that it won’t stuff a null into T — will cause code to silently
> change its behavior (and maybe its descriptor) when ArrayList is later
> specialized.  This puts pressure on our migration story; we want the migration
> of ArrayList to be compatible, and that means that things don’t subtly break
> when you recompile them.  Using ArrayList<V?> today means that even when
> ArrayList is specialized, this source utterance won’t change its semantics.

yes, you're right, but the downsize is that you have introduce V? in the type system and everybody has no to think if he should choose V or V? for every types.
Java has get ride of passing struct by value or ref because it simplifies the model, introducing V? creates such rift which i agree is comfortable for a designer because we don't break anything but at the same time we are offering to many options for something that will be seen as a subtle semantics issue for most of our users.

> 
> Essentially, erased generics type variables have an implicit bound of “T extends
> Nullable”; the migration from erased to specialized is what allows the
> declaration to drop the implicit bound, and have the compiler type-check the
> validity of it.

We don't need V? if generics are fully reified so introducing V? 20 years from now will look stupid.

> 
> We have four choices here:
> 
> - Don’t allow erased generics to be instantiated with values at all.  This sucks
> so badly we won’t even discuss it.
> - Require generics to certify their value-readiness, which means that their type
> parameters are non nullable.  This risks degenerating into the first, and will
> be a significant impediment to the use and adoption of values.

but nothing break in this mode.

> - Let users instantiate erased generics with values, and let them blow up when
> the inevitable null comes along.  That’s what you’re proposing.

until people annotate the method/class that doesn't support value type.

> - Bring nullity into the type system, so that we can accurately enforce the
> implicit constraint of today’s erased generics.  That’s what I’m proposing.

which doesn't scale because your asking all your users to think as API developers, not something i want to do when i just want to use an API.

> 
> 
> I sympathize with your concern that this is adding a lot of complexity.
> Ultimately, though, I don’t think just letting people blindly instantiate
> generics that can’t be proven to conform to their bounds is not helping users
> either.  Better suggestions welcome!

I believe that the issue is that V? should work as a box and currenty V? is to powerful/useful as a box so people will start to use it as a true type.

- V? should not be called V?, it's to short, you should have some ceremony that show you that V is the real deal, V.box was better
- V? should not be a supertype of V, again, it makes the box to powerful. With that peope wil  start to use V? as parameter type like we are using List instead of ArrayList
- you should not be able to call methods or fields on V? (constructor are still allowed), again it should be a box, so the implicit conversion from/to V/V? is fine, but not more.

the moto for V? should be works like an Integer (not more).

> 
> (A related concern is that V? looks too much like ? extends V, especially in the
> face of multiple tvars: Map<V?, ? extends U>. This may have a syntactic
> solution.).

Rémi


More information about the valhalla-spec-observers mailing list