Value types and nulls
Stephen Colebourne
scolebourne at joda.org
Fri May 18 21:04:57 UTC 2018
Going out on a limb here, I'd like to say that nulls are useful, and
value types need to have a similar concept.
Why? Because null is both a pain and useful. It is widely used to
represent an absence of something, and that concept applies just as
much to value types as reference types. For example, int has the
nullable java.lang.Integer as a companion that can be used when you
need a nullable int. Note that I'm no great lover of null - within our
codebase a null never crosses a compilation unit boundary. But we
still need the concept of nullable int (and nullable LocalDate).
Does it have to be null? Why not Optional? Or a typed null? Or a box?
Well maybe - I'm trying to keep on open mind on the exact way to
represent "absence", but I'll call it null for the rest of this email.
Thus, I think that <nullable-decimalVT> is absolutely necessary to
support <decimalVT> however it is spelled. What I really don't want is
that the nullable version is punished by having the same negative
performance/space cliff edge of int vs Integer. That would be hugely
undesirable.
On 15 May 2018 at 23:53, John Rose <john.r.rose at oracle.com> wrote:
> One way to look at it is that you are proposing two new kinds of
> concrete classes to go with our old object class.
>
> - object class = what we have today: identity-rich, nullable, not flattened
> - value class = new, flattened*, not nullable, identity-free
> - enforced value-based class = new, nullable, not flattened, identity-free
This looks pretty appealing as a starting point to me and worth
exploring. The key downside is the lack of flattening, which has the
negative performance/space trade-off I object to.
As discussed before though, the likelihood is that many value types
will have a spare bit pattern that could be used to mean null -
LocalDate and Currency certainly do for example. Whereas other value
types will not have a spare bit pattern Long128. Can this not be
embraced? Can we also say that its not just migration that needs this
- its more generally useful?
Value type category A:
- All bits significant, no spare bit pattern (eg. Long128), all-zero
bit pattern must have logical meaning
- Use site choice of non-null or nullable (non-null gets the better syntax)
- When the variable is use-site declared as non-null, the JVM flattens
as per the original plan. Arrays fully flattened.
- When the variable is use-site declared as nullable, the JVM still
flattens, but with one additional bit (probably padded to 32/64). I
suspect this effectively amounts to "subclass" of the value type with
an extended memory layout. Arrays of the nullable form use this
extended memory layout. While this isn't as flat it would be if it was
non-null, its still much better than being an array of references with
memory pointer hops.
- Long128 would use 128 bits per variable or array entry when declared
non-null, but might use 160 bits when nullable.
Value type category B:
- A spare bit pattern is available to use as null (eg. LocalDate)
- The JVM has "normal" NPEs when making method/field calls if the bit
pattern is all zeroes (null)
- Flattening happens, its just that the all-zero bit pattern actually
means null.
- Variables are always nullable. Arrays are therefore always nullable.
- Just like there is no way to express use-site nullability for
references, its not essential to express use-site nullability for
category B
Existing published classes should only be migrated to category B. This
allows the JVM to be much tougher when it finds mixed compilation
issues with category A (just throw Error).
Effectively, this gives developers the choice between:
- values that work much like existing classes ("code like a class, but
go faster")
- values that work much like primitives ("code like a class, works
like an int with a null-like friend").
This seems like a desirable choice for the long-term, not just a migration hack.
thanks
Stephen
More information about the valhalla-dev
mailing list