Null-restricted types: Why so complicated?
John Bossons
jbossons at gmail.com
Fri Jan 19 18:28:00 UTC 2024
Because the instance hasn't been defined yet. The array has been defined,
but not its elements.
On Fri, Jan 19, 2024 at 1:23 PM Quân Anh Mai <anhmdq at gmail.com> wrote:
> Hi,
>
> I still do not understand your question. By declaring an implicit
> constructor, you are indicating that the all-zero instance is usable, and
> by using Range![], you are telling Java that the all-zero instance is
> usable in this particular context. Why does the JVM need to protect you
> from invoking methods using an instance that you are specifically
> indicating as valid?
>
> Cheer,
> Quan Anh
>
> On Sat, 20 Jan 2024 at 02:05, John Bossons <jbossons at gmail.com> wrote:
>
>> Hi again,
>>
>> What you suggest is one solution. My counter: It's unsafe. The JVM should
>> be able to protect a developer from invoking doSomethingWith(a[5]) on an
>> undefined array element (or method parameter).
>>
>> On Fri, Jan 19, 2024 at 12:43 PM Quân Anh Mai <anhmdq at gmail.com> wrote:
>>
>>> Why not just
>>>
>>> Range[] a = new Range[100]; // allocate with null values
>>> System.out.println(a[5]); // NullPointerException
>>>
>>> By using Range![] you accept that a zero value is acceptable, similar to
>>> how an int[] works. If you do not want the uninitialized values to be
>>> usable then do not use null-restricted type.
>>>
>>> Cheer,
>>> Quan Anh
>>>
>>> On Sat, 20 Jan 2024 at 01:34, John Bossons <jbossons at gmail.com> wrote:
>>>
>>>> Hi Quan Anh,
>>>>
>>>> We're talking past each other.
>>>>
>>>> ME: But in Java idiom that means that a developer can invoke the
>>>> public implicit constructor, which will cause confusion.
>>>> YOU: An implicit constructor can be invoked like any other
>>>> constructor, and it will return an all-zero instance of the corresponding
>>>> class.
>>>>
>>>>
>>>> Precisely. Which will often not be valid in the application context. It
>>>> should be possible for a constructor to exclude an all-zero instance, such
>>>> as a zero-length Range or an all-null Name (to use two examples in the
>>>> draft spec) without giving up the ability to specify that it is
>>>> null-restricted. Or for the 'real' constructor to be private, invoked from
>>>> a factory method, which is effectively made useless as a protective feature
>>>> if a public constructor is also provided. (If the implicit constructor
>>>> could be specified as private, that would take care of the problem.
>>>> Extending a marker interface is simpler.)
>>>>
>>>> ME: > My further suggestion is that appending ! to a type should mean
>>>> that the default initialized value of an instance (all fields zero) is
>>>> equivalent to null, so that
>>>> > Range![] a = new Range![100]; // allocated with zero values
>>>> > System.out.println(a[5]); // throws NullPointerException
>>>> (zero fields)
>>>> > This better conforms to current idiom, where the initial
>>>> initialization is with nulls and the println invocation on a null array
>>>> element or field throws a NPE.
>>>> YOU: What is the value of this proposal? If you want the all-zero
>>>> instance to be equivalent to null, just do not have any constructor that
>>>> initializes an instance to that state. The whole point of null-restricted
>>>> fields/variables is to indicate that the field/variable is always valid.
>>>>
>>>>
>>>> The issue here is not what the constructor does -- it hasn't been
>>>> invoked yet for the element on which println is invoked -- but rather that
>>>> the fact that the array element is undefined should be capable of being
>>>> caught.
>>>>
>>>> On Fri, Jan 19, 2024 at 11:48 AM Quân Anh Mai <anhmdq at gmail.com> wrote:
>>>>
>>>>> I forgot to cc valhalla-dev
>>>>>
>>>>> ---------- Forwarded message ---------
>>>>> From: Quân Anh Mai <anhmdq at gmail.com>
>>>>> Date: Sat, 20 Jan 2024 at 00:33
>>>>> Subject: Re: Null-restricted types: Why so complicated?
>>>>> To: John Bossons <jbossons at gmail.com>
>>>>>
>>>>>
>>>>> Hi,
>>>>>
>>>>> > But in Java idiom that means that a developer can invoke the public
>>>>> implicit constructor, which will cause confusion.
>>>>>
>>>>> An implicit constructor can be invoked like any other constructor, and
>>>>> it will return an all-zero instance of the corresponding class.
>>>>>
>>>>> > My further suggestion is that appending ! to a type should mean that
>>>>> the default initialized value of an instance (all fields zero) is
>>>>> equivalent to null, so that
>>>>> > Range![] a = new Range![100]; // allocated with zero values
>>>>> > System.out.println(a[5]); // throws NullPointerException
>>>>> (zero fields)
>>>>> > This better conforms to current idiom, where the initial
>>>>> initialization is with nulls and the println invocation on a null array
>>>>> element or field throws a NPE.
>>>>>
>>>>> What is the value of this proposal? If you want the all-zero instance
>>>>> to be equivalent to null, just do not have any constructor that initializes
>>>>> an instance to that state. The whole point of null-restricted
>>>>> fields/variables is to indicate that the field/variable is always valid.
>>>>>
>>>>> I think you are having some confusion. Null-restriction is the
>>>>> property of a variable/field, i.e. the property of the holder, not of the
>>>>> class itself. The class having implicit constructors simply means that it
>>>>> allows the existence of null-restricted fields/variables. The class can be
>>>>> used as normal with non-null-restricted types. (e.g Range r = null;)
>>>>>
>>>>> Regards,
>>>>> Quan Anh
>>>>>
>>>>> On Sat, 20 Jan 2024 at 00:09, John Bossons <jbossons at gmail.com> wrote:
>>>>>
>>>>>> Thanks for your comments. I was not sufficiently explicit.
>>>>>>
>>>>>> Let me focus on implicit. I guess my dislike is of introducing a
>>>>>> 'fake' constructor into the definition of a class. I say 'fake' because, as
>>>>>> I understand it, the only purpose of the implicit constructor is to
>>>>>> indicate to the JVM/compiler that a never-null instance can be created. But
>>>>>> in Java idiom that means that a developer can invoke the public implicit
>>>>>> constructor, which will cause confusion.
>>>>>>
>>>>>> Maybe it would be better to require a potentially null-restricted
>>>>>> class to extend a marker interface ('extends NeverNullPossible'? Or maybe,
>>>>>> looking ahead to my next comment, 'extends AllZerosIsNull'?). That would
>>>>>> enable the compiler to catch an invalid use of the ! marker in a
>>>>>> declaration, just as the proposed implicit constructor does, while
>>>>>> conforming better to common Java idiom.
>>>>>>
>>>>>> My further suggestion is that appending ! to a type should mean that
>>>>>> the default initialized value of an instance (all fields zero) is
>>>>>> equivalent to null, so that
>>>>>> Range![] a = new Range![100]; // allocated with zero values
>>>>>> System.out.println(a[5]); // throws NullPointerException
>>>>>> (zero fields)
>>>>>> This better conforms to current idiom, where the initial
>>>>>> initialization is with nulls and the println invocation on a null array
>>>>>> element or field throws a NPE.
>>>>>>
>>>>>> As you say, my suggestion means runtime testing to determine if all
>>>>>> fields are zero, which has a performance cost. This will only occur if the
>>>>>> JVM implements the ! specification, which it presumably will only do if the
>>>>>> object is small. And the cost will be small (I am presuming) relative to
>>>>>> savings from allowing the memory footprint to match that of primitives. Am
>>>>>> I wrong? There is value in conforming to current idiom.
>>>>>>
>>>>>> Turning to the LooselyConsistentValue, I withdraw my comments. I
>>>>>> mistakenly presumed that its use would be required, which is false. It
>>>>>> simply enables a single-threaded (or volatile-protected) application to
>>>>>> allow additional inlining, which is harmless.
>>>>>>
>>>>>> John
>>>>>>
>>>>>> On Thu, Jan 18, 2024 at 4:56 PM - <liangchenblue at gmail.com> wrote:
>>>>>>
>>>>>>> Hi John,
>>>>>>>
>>>>>>> On Thu, Jan 18, 2024 at 2:30 PM John Bossons <jbossons at gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi all,
>>>>>>>>
>>>>>>>> Maybe I am missing something, but the proposal seems to be trying
>>>>>>>> to do too much.
>>>>>>>>
>>>>>>>> Specifically: Why not simply provide that appending ! to a type
>>>>>>>> specification for an object (field, array element, or parameter) means that
>>>>>>>> that the object is not only null-restricted but also never zero and
>>>>>>>> necessarily non-atomic unless small?
>>>>>>>>
>>>>>>> First, a reminder that some objects cannot be non-atomic, mostly
>>>>>>> when fields have dependencies/constraints on each other: if you have a
>>>>>>> range, you cannot allow its lower bound to be larger than its upper bound.
>>>>>>> Non-atomic representations cannot avoid this pitfall. Also you seem
>>>>>>> to misunderstand non-atomic: if an object is non-atomic, each of its fields
>>>>>>> can update independently from each other, so a 3-d position can be
>>>>>>> non-atomic, but not so for a range. Non-atomicity is dangerous, and it
>>>>>>> should not be the default. However, if an atomic class is small enough,
>>>>>>> like OptionalInt (as now many architecture has like atomic handling of 16
>>>>>>> bytes etc.) JVM may choose to apply non-atomic optimizations to them for
>>>>>>> better performance without violating their object constraints.
>>>>>>>
>>>>>>>>
>>>>>>>> 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?
>>>>>>>>
>>>>>>> The implicit constructor can always be called; its existence asks
>>>>>>> programmers to affirm that the zero-filled inlined instance is a valid
>>>>>>> instance. And this instance is different from a null, as null is a pointer,
>>>>>>> yet the zero-instance has a different size defined by the class layout in
>>>>>>> the stack/heap.
>>>>>>>
>>>>>>>>
>>>>>>>> Sure, that means trying to read a zero value triggers a NPE. That
>>>>>>>> just means that a type that can legitimately have a zero value cannot be
>>>>>>>> specified as null-restricted, since a zero value (e.g. a {null, null} Name)
>>>>>>>> is the equivalent of a null unrestricted value object. Why go beyond that?
>>>>>>>> If a non-null zero value is possible, the type cannot be null-restricted
>>>>>>>> and so can only be an unrestricted JEP 401 value type. End of story.
>>>>>>>>
>>>>>>> You see the inlined zero instance and the null pointer have
>>>>>>> different sizes, and thus they are not exchangeable. Converting the inlined
>>>>>>> zero instance to null to throw NPE is complex and hurtful to performance as
>>>>>>> you will scan unrelated bits for almost every field access.
>>>>>>>
>>>>>>> And for unrestricted value type, yes, they exist and can possibly be
>>>>>>> inlined as well if the restricted type is small enough (i.e. has space for
>>>>>>> extra bit indicating nullity) But reminder, the nullity bit itself isn't
>>>>>>> even non-atomic with (depends on) the rest of the object! You don't want
>>>>>>> the nullity to indicate null while the rest of the object indicate some
>>>>>>> sort of non-null value, which can happen in a non-atomic context.
>>>>>>>
>>>>>>>>
>>>>>>>> With respect to non-atomic, what is new? Yes, unexpected instances
>>>>>>>> may occur without synchronization if the object is larger than the word
>>>>>>>> size of the implementation. Why do we need to extend a
>>>>>>>> LooselyConsistentValue interface to know/permit that?
>>>>>>>>
>>>>>>> Unexpected instances don't occur without synchronization if you use
>>>>>>> finals, such as in Java's String or immutable List.of(). These APIs may
>>>>>>> capture any "permitted value" from the arrays passed in, but once
>>>>>>> constructed, the captured value remains constant no matter which thread
>>>>>>> observes the String/List object reference. (Technically, JVM implements
>>>>>>> this with a store-store fence between end of field writes in the
>>>>>>> constructor and object reference is shared anywhere, and a load-load fence
>>>>>>> between object reference read and field read) Value classes is about the
>>>>>>> safety of final fields in programming instead of the close encounter of
>>>>>>> third kinds of synchronization, volatiles, and fences.
>>>>>>>
>>>>>>>>
>>>>>>>> Can we not keep this 'simple' (if that word has meaning in this
>>>>>>>> context)? What am I missing?
>>>>>>>>
>>>>>>> I think you are missing a bit about how the layout (inlining is
>>>>>>> represented in memory) and value classes (the thread safety its final
>>>>>>> offers) work, and what "non-atomic" means. Feel free to question more.
>>>>>>>
>>>>>>>>
>>>>>>>> John
>>>>>>>>
>>>>>>>>
>>>>>>>> --
>>>>>>>> Phone: (416) 450-3584 (cell)
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>> --
>>>>>> Phone: (416) 450-3584 (cell)
>>>>>>
>>>>>
>>>>
>>>> --
>>>> Phone: (416) 450-3584 (cell)
>>>>
>>>
>>
>> --
>> Phone: (416) 450-3584 (cell)
>>
>
--
Phone: (416) 450-3584 (cell)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240119/3ec2234b/attachment.htm>
More information about the valhalla-dev
mailing list