Null-restricted types: Why so complicated?

Brian Goetz brian.goetz at oracle.com
Fri Jan 19 21:34:50 UTC 2024


> Thanks for your comment and the reference to Brian's 2021 document. 
> You're right about the series of steps, I get that. My comments on 
> undefined array elements really come down to saying that it's bad 
> practice to specify arrays of null-restricted elements except where 
> you're sure undefined elements are never read before being defined.

I think part of why you are confused here is that you are looking at the 
features individually, rather than the big picture.

Identity restriction is both semantically useful, and potentially admits 
certain optimizations.
Implicit construction is semantically useful (it says "you can use me 
without initialization", just as types like `int` do), and potentially 
admits certain optimizations.
Non-nullity is semantically useful, and potentially admits certain 
optimizations.

We do not want to define "performance features" here; we want to define 
semantically useful features of classes, for which we can reliably 
perform desired optimizations when the right semantic constraints are in 
place.  The hard part was identifying which semantic features those were.

Fields of type `String!` (and arrays whose elements are `String!`) are 
semantically useful (lots of people would love to have them even if they 
never get value types), even though we can't flatten them at all.  But 
to ensure their usefulness, we need to ensure they are initialized 
before use (or, tolerate NPEs in badly written programs, such as when 
`this` escapes construction.)

On the other hand, fields of type `Integer!` (or arrays whose elements 
are `Integer!`) can be flattened safely regardless of races, 
this-escapes, etc -- because `Integer` is implicitly constructible.  
(And, this also allows us to guarantee that it will *never* be null, as 
opposed to the `String!` case, where such a guarantee always comes with 
caveats.)  But the cost of this is that we have to know which classes 
are like String and which are like Integer, and the implicit constructor 
is what signals this.

On the other other hand, fields/arrays of `LocalDate!` _could_ be 
flattened, but only if we are absolutely 100% sure (no caveats) that 
they are guaranteed to be initialized before writing.  (Failure to get 
this right is much worse than an errant NPE; we've violated the 
integrity of the object model.)

We want the language and runtime to have access to all the information 
it needs to provide good performance and safety in all of these cases.  
You might dislike the syntax choice behind implicit constructors (maybe 
you prefer a class modifier like `implicitly-constructible`), but this 
isn't the place for syntax debates.  The important thing here is that 
whether a class can be implicitly constructed or not appears, after many 
many hours of analysis, to be an imcompressible element of the design, 
unless we are willing to compromise on either safety or flattening.  It 
is natural and healthy to wonder "can this be simplified further" ... 
but best to do so with awareness that we may be staring at Chesterton's 
Fence (https://fs.blog/chestertons-fence/).



More information about the valhalla-dev mailing list