Null-restricted types: Why so complicated?

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Jan 19 19:02:29 UTC 2024


Hi John

On 18/01/2024 19:38, John Bossons wrote:

> Why complicate the specification with an implicit constructor that a 
> developer will never explicitly invoke? Why permit a developer to 'opt 
> in' to non-atomic?

I believe you are coming at this from the wrong angle. The implicit 
constructor is, at its core, a mechanism for the class to advertise that 
it has a zero instance (as the JEP calls it [1]).

This means that whenever a default value of the class has to be 
materialized it’s as if the impliict constructor was called.

This are some of the ways this can happen (there’s probably more):

  * when you allocate arrays
  * when you create a |MethodHandle| which returns the zero value of a
    given class (see |MethodHandles::zero|)
  * if we ever provide a syntax to say |Foo.default|/|Foo.zero|

In other words, once a value class has an associated zero-instance, 
developers can materialize that zero-instance in many different ways. 
Because of that, the point you raise that implicit constructors make you 
declare a constructor that you don’t want your clients to invoke is a 
bit off the mark, because clients can maetrialize zero-instances anyway. 
Actually, advertising it as an API point makes it quite clear: the 
maintainer of that value class has to be prepared to handle cases where 
all fields are zeroed - that’s part of its API contract.

Zooming back a bit, you seem to favor a model where the cost is pushed 
to every read of an array element, or flattenable field. Reads are (way) 
more common that writes, which is why the current proposal only lets you 
build array types (and flattenable fields) which make sense given the 
value type under consideration. If you want to build a non-null 
container, fine, but the type of the container must better support the 
“all zero” configuration, since that’s the paintbrush the JVM will use 
to paint the container bits. This means that, array creation of a 
null-restricted value is the same as for a regular identity class (all 
bits zeros) and, since the value class explicitly declared its support 
for the zero-instance configuration, no check is required on reads.

And, zooming back even more (now we’re up in the clouds :-) ), the model 
we’re trying to achieve here is one where you get more (flattening) 
guarantees, by progressively giving up other guarantees. For example:

  *

    opt-out of identity (use |value|) -> enables better
    flattening/scalarization (on the stack), allow flattening (but with
    some extra bits to handle nulls)

      o opt-out of zero-instance protection (use implicit constructors)
        -> removes the extra null state from the flattened storage
      o opt-out of nullability (use null-restricted types) -> allows
        flattening of fields/array elements
      o opt-out of atomic updates (use non-atomic) -> allows flattening
        of types that are bigger than N (where N defined by the JVM)

    You can see this as a series of step that takes you, smoothly, from
    an identity class such as |java.util.ArrayList|, down to something
    simpler and simpler, until you get to |int|, a primitive type
    (which, not coincidentally, has given up exactly the same guarantees
    listed above!). That is, maximum flattening (e.g. int-like) is not a
    simple on/off switch, but, rather, a property that emerges when the
    treatment of the value being flattened can be relaxed along /all/
    the axis shown above. This model is described, in much better words
    than the ones I used here, in Brian’s document [2].

Maurizio

[1] - https://openjdk.org/jeps/8316779
[2] - 
https://github.com/openjdk/valhalla-docs/blob/main/site/design-notes/state-of-valhalla/02-object-model.md

​
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240119/89b57cca/attachment.htm>


More information about the valhalla-dev mailing list