Default value for date/time
John Rose
john.r.rose at oracle.com
Mon May 7 23:47:39 UTC 2018
This is a good discussion! We need to feel our way to some
rules of thumb for migrating VBCs like LD to proper value types.
On May 7, 2018, at 4:08 PM, Stephen Colebourne <scolebourne at joda.org> wrote:
>
> On 7 May 2018 at 19:27, John Rose <john.r.rose at oracle.com> wrote:
>> The VM mandates that the all-zero encoding must be accepted
>> by all value types. This is a forced move as you probably know.
>> We have to define what this expression produces:
>> LocalDate dflt = (new LocalDate[1])[0];
>
> null?
Nope, null is a reference, and is not any kind of value.
Our key principle is "codes like a class, works like an int."
Null doesn't interconvert with primitives (except via boxing)
so neither will it interconvert with value types.
Making value types (or primitives) nullable is a separate
project, one we don't want to undertake.
> throws an exception? (ie. the default can't be observed)
"Works like an int" means there is always an observable
default. Making defaults unobservable is not "like an int",
and is not a project we want to undertake either.
> And we won't allow the value type itself to define an ad hoc default,
>> because the implementation costs are high and the programmer
>> convenience is marginal.
>>
>> I suggest that, for LocalDateTime, the required default (all-zero)
>> throw exceptions for all component accessors. Alternatively, the
>> zeroes could be taken to encode a "dumb" date like 1970-01-01.
>
> LocalDate objects are always valid - you can't create one representing
> the 31st February for example. So having an "instance" that exists in
> a local variable but is invalid, and throws from all methods seems
> pretty terrible.
Well, that's the status quo today, if you look at LocalDate as a VBC:
LocalDate ld = (new LocalDate[1])[0];
assert(ld == null); // hey, that breaks the VBC rules!
System.out.println(ld.year()); // => NPE
In other words, the default value of the *reference type*
LocalData throws NPE on all operations, except certain
comparison and string-rendering operations, where you
find out that it is a null.
When migrating LocalDate to a proper value type, the
above behavior could be very closely approximated by
these steps:
- make all-zero LD report equality only with itself for Object.equals
- make all-zero LD return "null" for toString
- make all-zero LD throw NPE for all other access methods
All of the points but the first are likely to be adjusted to something
better, such as:
- make all-zero LD return "LocalDate(null)" for toString
- make all-zero LD throw NullValueException for all other access methods
(Where NVE is a relative of NPE.)
> Structuring the internal data such that all zeroes represents
> 1970-01-01 and that no other bit pattern represents that date would
> also be a bit painful (not too painful in this case, but I could
> imagine other value types where it would be).
Yep. Dan Smith points out we could push some of the pain
into the VM by allowing users to specify their own defaults
and asking the VM to invisibly XOR the fields of a value type
with the default value, when stored in the heap. Again, marginal
usability gains, at the cost of an obscure performance model.
> In essence, there would
> have to be a `yearMinus1970`, 'monthMinusOne` and `dayMinusOne` field
> instead of a `year` field - not very "codes like a class". I would
> have naturally expected that the choice of default was under
> programmer control, but if it can't then so be it.
Too difficult. Zeroing memory is cheaper than stenciling ad hoc
patterns. Ad hoc defaults cause an invisible global slowdown.
> Maybe what I'm asking is whether nullable value types are feasible,
> but I'm certain I don't understand all the associated trade offs.
The tradeoffs are tricky. Avoiding stencils is just one consideration.
Systematic nullability would require a protocol which we don't want to
design if we don't have to. I showed you above how to approximate
the behavior of nulls using ad hoc code. Maybe we can make an
implementation pattern like that easy to mix into record types, too.
Also, the default value of Optional very cleanly boils down to empty,
since the reference is null (and/or any extra boolean would be false).
In the case of Optional we'd want the default value to play a very
definite role in the API.
So making default values disappear or convert to null, always, is the
wrong answer, and not just for the primitives we are modeling them on.
Bottom line: Default value policy is a per-class problem for value types.
To migrate to value types, you have to design and implement type-specific
rules for default values.
— John
More information about the valhalla-dev
mailing list