[External] : Re: User model stacking

Remi Forax forax at univ-mlv.fr
Thu Apr 28 13:09:38 UTC 2022


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Dan Heidinga" <heidinga at redhat.com>
> Cc: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
> Sent: Thursday, April 28, 2022 1:15:08 AM
> Subject: Re: [External] : Re: User model stacking

> Let me try and put some more color on the bike shed (but, again, let’s focus on
> model, not syntax, for now.)

> We have two axes of variation we want to express with non-identity classes:
> atomicity constraints, and whether there is an additional zero-default
> companion type. These can be mostly orthogonal; you can have either, neither,
> or both. We've been previously assuming that "primitiveness" lumps this all
> together; primitives get more flattening, primitives can be
> non-nullable/zero-default, primitives means the good name goes to the "val"
> type. Primitive-ness implicitly flips the "safety vs performance" priority,
> which has been bothering us because primitives also code like a class. So we
> were trying to claw back some atomicity for primitives.

> But also, we're a little unhappy with B2 because B2 comes with _more_ atomicity
> than is necessarily needed; a B2 with no invariants still gets less flattening
> than a B3. That's a little sad. And also that it seems like a gratuitous
> difference, which makes the user model more complicated. So we’re suggesting
> restacking towards:

> - Value classes are those without identity
> - Value classes can be atomic or non-atomic, the default is atomic (safe by
> default)
> - Value classes can further opt into having a "val" projection (name TBD, val is
> probably not it)
> - Val projections are non-nullable, zero-default — this is the only difference
> - Both the ref and val projections inherit the atomicity constraints of the
> class, making atomicity mostly orthogonal to ref/val/zero/null

Now that the model is clearer, let's try to discuss about the val projection. 

Once we have universal generics, we will have an issue with value type with zero-default, there are a lot of API in the JDK that explicitly specify that they return/pass null as parameter, 
by example, Map.get(), for those call, we need a way to say that the type is not T but T | null. 
The current proposal is to use T.ref for that. 

Now, Kevin and Brian thinks that for zero-default value type, in the language, Complex.val should be used instead of Complex. 
Lets see how it goes 
1/ There is a difference between Foo and Foo.ref for generics, Foo is a class while Foo.ref is a type. 
The idea of using Complex.val means that the relationship is reversed, 
Complex is the type and Complex.val is the class. 
If we ride with that horse, it means that in universal generics, we should not use T but T.val apart when we want T.val | null that can be spelled T. 

2/ Because Complex.val is a class and Complex is a type, we have a weird dis-symmetry, 
User will declare a class Complex, but to create a Complex, they will have to use new Complex.val(). 
As a user this is weird. 

3/ This may change but currently, Foo.class exists but Foo.ref.class is not allowed, you have to use a method to get the projection, 
something like Foo.class.getMeTheProjectionPlease(). 
With .val being the default, it means that Complex.val.class exists while Complex.class does not. 
Same to get the default value, Complex.class.getDefaultValue() will not compile, it should be Complex.val.class.getDefaultValue(). 
Again weird. 

4/ It's a double opt-in, people have to opt-in at declaration site by asking for a zero-default value type but that is not enough, 
it only works if the type val is uses at use site. I don't know any feature in Java that requires a double opt-in. 

5/ It's easy to forget a ".val". To work, people will have to pepper .val everywhere and it will be easy to miss one occurrence. 
Depending on where the ".val" is missed, performance will suffer. This is something i see when beginners dab with generics 
for the first times, the compiler emits a warning because they miss one pair of <> somewhere in the code. 
To avoid missing , compilers/IDEs will try to rely on null analysis to emit a warning when Complex.val should be used instead of Complex. 
Again, the relationship is seen in the wrong direction, with .ref, you get performance by default, the compiler does not compile 
if you try to return/store null so by design the compiler helps you to write the right code. 

6/ Zero-default value type does not imply non-atomic anymore, 
so a zero-default value type is not more dangerous that a null-default value type anymore. 

regards, 
Rémi 


More information about the valhalla-spec-observers mailing list