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.

I share this concern.  Do you have any thoughts of how to make B2 more attractive?

For the record, we expect to see similar stack-based and IR-based flattening across buckets 2 and 3, but measurably less heap-based flattening for bucket 2.  Plus the extra footprint.

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

B2 gets state-based ==, just like B3.  No difference there; if you have no identity, then equality is state-based.



    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.

Yes.  A B2, and a B3.ref, behave identically.

(I'm aware you intend to define `==` differently for the two, but I'll get into my massive concerns about that later.)

Actually, B2, B3, and B3.ref all have the same interpretation of ==, which is state-based.  (You can think of this as "box (or unbox) before comparing a B3 with a B3.ref.)


     - 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)

Yeah, maybe.  If that were the only difference, I'd be more inclined.  But it drags in ref-ness, and all the reference affordances, so it feels more misleading than helpful at this point.


    ## 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).

There's a nasty tension here.  On the one hand, for B3 classes, it makes sense for b3.getClass() to yield the val mirror, but int.getClass() historically corresponds to the ref mirror (Object o = 3; o.getClass() == Integer.class.)  To invert it, we would have to break a lot of reflection-using code that tests for Integer.class because that's how primitives are reflected.  Work in progress.


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.)

You could argue that it doesn't make sense on the values, but surely it makes sense on their boxes.  But its a thin argument, since classes extend Object, and we want to treat values as objects (without appealing to boxing) for purposes of invoking methods, accessing fields, etc.  So getClass() shouldn't be different.

Reply via email to