Value types design decisions
Christoph Engelbert
me at noctarius.com
Sat Jan 20 15:38:39 UTC 2024
Hey Wilson,
As an addition to what Brian said, it would be totally possible to write your Date class in a way that an all-zero’d value would be interpreted as 1970-01-01. In this case the internal representation would not represent the actual items of the date, but the offset to some specific epoch (which is what most date implementations do anyways).
In your case an all-zero offset means no offset to the epoch which starts at midnight Jan 1st, 1970.
I think that is something to keep in mind. If my default can be considered to be some kind of offset, my zero’d value can always represent that default.
Cheers,
Chris
> On Jan 20, 2024, at 16:24, Brian Goetz <brian.goetz at oracle.com> wrote:
>
> (1) and (2) are really the same question, and reflect a misunderstanding of what an implicit constructor is. I think what you are hoping for is that you would be able to select the _default value_ used to initialize freshly allocated memory, rather than the VM-selected all-zero. This is not what an implicit constructor means, because, unfortunately, this is not consistent with the physics of the problem; the VM initializes memory with a wide roller, not a fine brush. While I can certainly sympathize with the _desire_ to have this, it's not really practical.
>
> So what *is* an implicit constructor? First, let's understand what happens when you say `new Foo(3)`. An uninitialized object is allocated and zeroed out, and then the Foo(int) constructor is run to initialize its state. The runtime takes some care (though badly written constructors can undermine this) to ensure that the object is not exposed to "regular code" before initialization has completed.
>
> An implicit constructor is a statement by the class author that "calling the constructor is optional"; in other words, that the uninintialized object allocated by the JVM is a perfectly valid instance, and a good default value. (Many important classes fit this description, such as nearly all numerics, and some library classes like Duration and Optional.) In turn, this means it is safe for the VM to expose an otherwise-uninitialized object, which in turn, enables richer flattening optimizations. Since it is a statement that "construction is optional", having a body would make no sense, since the constructor might never be called. And since construction is optional, the "make me one without construction" is an inherently public operation (to the extent that the class itself is public). You can't restrict it without restricting things like "no one can declare a field of type Foo".
>
> We went back and forth between representing this as a special constructor, and representing this as a modifier on the class (e.g., `implicitly-constructible`.) Each had pros and cons (but this isn't the time or place to discuss syntax choices). I suspect that you (though not everyone) would have found the "class modifier" route to be more evocative of what is really going on, though had we gone the other way, there would be different complaints.
>
> Of course, you _can_ omit the implicit constructor, and write an ordinary no-arg constructor that lets you set your preferred "default" values. But the cost of that might be not getting all the memory flattening you might hope for. Valhalla, unlike some other projects, is more constrained by the physics of object layout and allocation, and as such, exposes some difficult tradeoffs. (Previously, the VM made these tradeoffs for you, often conservatively.)
>
> To your third question, yes, this syntax is controversial. (The alternatives are not necessarily better, and everything you propose has already been discussed. As always, we may reconsider this after gathering more experience.)
>
> (As an aside, I would suggest that when confronted with something that seems confusing or surprising, to strive to fully understand before reaching for language like "make the same mistake again.")
>
> Cheers,
> -Brian
>
> On 1/20/2024 2:00 AM, Smith Wilson wrote:
>> Hello Valhalla community,
>> I have reviewed the latest documents on the project and have some concerns about current design decisions.
>>
>> 1) Implicit constructor cannot have body.
>> While I understand why we need implicit ctor and why it must have no args, I still don't understand why we can't allow users to set default values on their own using that ctor instead of implicitly defaulting them to (0, false, null). It could solve problems with such classes like LocalDate which require more special zeroInstance (1970-1-1) than value with pure defaults.
>> I believe that even String class could be somehow adopted to be value class in future having default null-restricted value of "" with (byte[] value) assigned to empty byte array. Non-final fields (hash, hashIsZero) could be placed into separate final field (i.e. hashHolder) given that it will be forcedly flattened, so there will be no overhead.
>>
>> 2) Implicit constructor must be public.
>> We all learned that overextending public apis can lead to problems. Great example of this is wrapper classes whose constructors have been deprecated long time ago and still causing a lot of problems. So, new classes (i.e. Optional) were designed to have static factories (Optional#of, Optional#empty), rather than exposing their internal constructors. (Moreover, constructor calls use different byte-code instructions than method calls, which also can cause byte-code incompatibilities in case of future migrations.)
>> I don't understand why we are going to make same mistake again and why we can't allow implicit constructors to have any kind of visibility modifier. So, VM will be able to freely use zeroInstances where necessary, while user himself will be able to control use of class exposing special apis.
>>
>> 3) Using interface (LooselyConsistentValue) to express non-atomicness.
>> Same story as with Serializable interface. It is considered that using marker-interface for such a problem was bad design. Although this was justified by the fact that we did not have annotations, it is now unclear what makes us to use interfaces again. While it is possible to come up with real-life use cases of Serializable where type restrictions may be required (some usage of ObjectI/OStream apis), for such VM-close features like non-atomicness there is no real need for such opportunity. (Moreover, we already have some inconveniences because of that. In some cases, type inference of "var" is already blowing up from large type unions like Number & Comparable & Serializable.)
>> So, I believe we should use alternatives like class modifier or annotation, rather than polluting the type system for no reason.
>>
>> Regards,
>> Wilson.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240120/8b2a02bc/attachment.htm>
More information about the valhalla-dev
mailing list