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