Consolidating the user model

Kevin Bourrillion kevinb at google.com
Tue Nov 2 22:44:34 UTC 2021


Good stuff.


On Tue, Nov 2, 2021 at 2:19 PM Brian Goetz <brian.goetz at oracle.com> wrote:

But, the "traditional" primitives are not nullable, and for good reason;
> zero is
> a fine default value,
>

Yes, it would have been impractical to do otherwise, but here's my stock
reminder that zero being a "fine" default value has *still nonetheless* caused
many thousands of bugs.

Again, it had to be done. But I think it's notable that those bugs happen
even for the types that have the *absolute most sensible* default values.

My concern is that the purest form of value types will be overused and
misused for even less clear-cut cases. I would like to think that we can
convince these users that they really want the next "bucket" over, which I
think comes down to whether the added cost of `null` is worth it.


Returning to the tempting user knobs of nullability and tearability, we can
> now
>
put these where they belong: nullability is a property of _reference types_
> --
>

Though I've argued loudly here for the notion that nullability is not
*conceptually* intrinsic to references (and though I still think we should
start saying "the null value" instead of "the null reference"), I
nevertheless find this an acceptable compromise, because (a) I think
nullable values was just introducing too much practical complexity (b) I
hope most use cases really will just use the middle bucket and be fine.

Btw, am I right that for the middle bucket, `==` will fail (at compile-time
when possible)?


The third bucket are the _true primitives_.  These are also identity-free
> classes, but further give rise to both value and reference types, and the
> value
> type is the default (we denote the reference type with the familiar
> `.ref`.)
> Value types are non-nullable, and permit tearing just as existing
> primitives do.
> The `.ref` type has all the affordances of reference types -- nullability
> and
> tearing protection.
>

In fact, if I'm looking at a middle-bucket class, and I'm looking at one of
these `.ref` types of "primitive" class, as far as I can tell I should be
able to think of these in exactly the same way as exactly the same things.
(I'm aware you intend to define `==` differently for the two, but I'll get
into my massive concerns about that later.) Basically, that's good.



> How we describe the buckets is open to discussion; there are several
> possible
> approaches.  One possible framing is that the middle bucket gives up
> identity,
> and the third further gives up references (which can be clawed back with
> `.ref`), but there are plenty of ways we might express it.
>

We should address the conceptual-simplicity cost of this "clawing back"
sometime.


Another open question is whether we double down, or abandon, the
> terminology of
> boxing.  On the one hand, users are familiar with it, and the new
> semantics are
> the same as the old semantics; on the other, the metaphor of boxing is no
> longer
> accurate, and users surely have a lot of mental baggage that says "boxes
> are
> slow."  We'd like for users to come to a better understanding of the
> difference
> between value and reference types.
>

The key for me is that the new boxing takes over for everything the old
boxing did, and more. So, it's better boxing. I see no value in fighting
against that. If users are thinking of this by starting from what they know
about int/Integer, that's actually *good*. They will just find out it's
better, that's all.


 - Null-adjunction.  Some methods, like `Map::get`, return null to indicate
> no
>    mapping was present.  But if in `Map<K,V>`, `V` is not nullable, then
> there
>    is no way to express this method.  We envision that such methods would
> return
>    `V.ref`, so that strict value-based classes would widened to their
> "box" on
>    return, and null would indicate no mapping present.
>

Now just spell it `?` :-)

(not serious. Also, not not serious)


## Reflection
>
> Earlier designs all included some non-intuitive behavior around
> reflection.
> What we'd like to do is align the user-visible types with reflection
> literals
> with descriptors, following the invariant that
>
>      new X().getClass() == X.class
>

Seems like part of the goal would be making it fit naturally with the
current int/Integer relationship (of course, `42.getClass()` is uncommitted
to any precedent).

It seems like `Complex.class` (as opposed to `Complex.ref.class`) would
never be returned by `Object.getClass()` in any other condition than when
you could have just written `Complex.class` anyway.

Actually, that makes me start to wonder if `getClass()` should be another
method like `notify` that simply doesn't make sense to call on value types.
(But we still need the two distinct Class instances per class anyway.)


-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com


More information about the valhalla-spec-observers mailing list