All else being equal, the idea to use "inaccessible value type" over "value type doesn't exist" feels very good and simplifying, with the main problem that the syntax can't help but be gross.

Yep.

* It feels wrong to restrict access to the type only because of two very specific things we don't want people to do with the type. We don't want them to write `new TheType.val[size]`, and we don't want them to write `TheType.val someUnintializedField;`. Is there a third? And can we really not just prevent those specific things? It feels like baby/bathwater, especially since delayed initialization scenarios like those are already problematic in many ways as it is.

This is where I started; that the thing really being protected against is _creating heap locations with their default value_, which is done by fields and by array creation expressions.  (When we get to specializable generics, there will be a third: using the .val class as a type parameter, since `Foo<T>` might have uninitialized T-valued fields, so a `Foo<P.val>` could have the same problem as any other class that declares a `P.val` field.)

The reason I backed off is that this seemed (a) a hard border to explain, (b) calling users attention to low-level details like heap vs stack values, and (c) the value of being able to use P.val here-but-not-there is not all that strong.

Hard to explain.  Understanding this requires understanding a lot of low-level things that many Java developers have never thought about, such as heap vs stack, or the fact that, even if a static field has an initializer, you can still observe its default value with unlucky timing.  Drawing the border to keep the zeroes out will require a lot of context, focus on details we would rather users not dwell on, and will invariably be perceived as a "weird" restriction.  Whereas "this type is private, you can't see it" seems pretty normal.

Small incremental benefit.  This one requires appealing to the low-level details, but basically, the benefit of using P.val instead of P.ref in stack contexts (method parameter and returns, locals) appears to be pretty small, because of the excellent calling convention optimization that we get even on (preloaded) L-types of value classes.  The jury is out on "appears", but it is looking that way.  So the argument of "don't sweat the difference between P.ref and P.val except in the heap" seems reasonable.  \

Similarly, if P wants to, it can dispense safely constructed instances of P.val[], which are covariant to the exported P.ref[].

So this felt like a reasonable "worse is better" move, but I'm open to new ideas.

* I still am saddled with the deep feeling that ultimate victory here looks like "we don't need a val type, because by capturing the nullness bit and tearability info alone we will make /enough/ usage patterns always-optimizable, and we can live with the downsides". To me the upsides of this simplification are enormous, so if we really must reject it, I may need some help understanding why. It's been stated that a non-null value type means something slightly different from a non-null reference type, but I'm not convinced of this; it's just that sometimes you have the technical ability to conjure a "default" instance and sometimes you don't, but nullness of the type means what it means either way.

Here's the chain of reasoning that works to get to this state of affairs.

 - Reference types don't tear.  The JMM gives us strong safety guarantees about references to objects with final fields.  We want this to work the same way for references to value objects as well as references to identity objects, because otherwise, the "immutability means thread safety" promise is undermined.

 - P.ref is a reference type; P.ref[] is an array of references.

 - For non-atomic value classes, P.val fields can tear under race (and similarly elements of P.val[]).

 - If we spelled .val as !, then switching from P[] to P![] not only prohibits null elements, but changes the layout and _introduces tearing_.  Hiding tearability behind "non-null" is likely to be a lifetime subscription to Astonishment Digest, since 99.9999 out of 100 Java developers will not be able to say "non-null, oh, that also means I sacrifice atomicity."

The link you probably want to attack is this last one, where you are likely to say "well, that's what you opted into when you said `non-atomic`; you just happen to get atomicity for free with references, but that's a bonus."

Reply via email to