[External] Foo / Foo.ref is a backward default; should be Foo.val / Foo
Kevin Bourrillion
kevinb at google.com
Tue Apr 26 23:22:17 UTC 2022
On Mon, Apr 25, 2022 at 10:05 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> > 1. The option with fewer hazards should usually be the default. Users
> won't opt themselves into extra safety, but they will sometimes opt out of
> it. Here, the value type is the one that has attendant risks -- risk of a
> bad default value, risk of a bad torn value. We want using `Foo.val` to
> *feel like* cracking open the shell of a `Foo` object and using its innards
> directly. But if it's spelled as plain `Foo` it won't "feel like" anything
> at all.
>
> Let me state it more strongly: unboxed “primitives” are less safe.
> Despite all the efforts from the brain trust, the computational physics
> still points us towards “the default is zero, even if you don’t like that
> value” and “these things can tear under race, even though they resemble
> immutable objects, which don’t.” The insidious thing about tearing is that
> it is only exhibited in subtly broken programs. The “subtly” part is the
> really bad part. So we have four broad options:
>
> - neuter primitives so they are always as safe as we might naively hope,
> which will result in either less performance or a worse programming model;
> - keep a strong programming model, but allow users to trade some safety
> (which non-broken programs won’t suffer for) with an explicit
> declaration-site and/or use-site opt-in (“.val”)
> - same, but try to educate users about the risk of tearing under data
> race (good luck)
> - decide the tradeoff is impossible, and keep the status quo
>
> The previous stake in the ground was #3; you are arguing towards #2.
I'm confused here -- I don't recognize that as being what I'm arguing.
> > 2. In the current plan a `Foo.ref` should be a well-behaved bucket 2
> object. But it sure looks like that `.ref` is specifically telling it NOT
> to be -- like it's saying "no, VM, *don't* optimize this to be a value even
> if you can!" That's of course not what we mean. With the change I'm
> proposing, `Foo.val` does make sense: it's just saying "hey runtime, while
> you already *might* have represented this as a value, now I'm demanding
> that you *definitely* do". That's a normal kind of a thing to do.
>
> A key aspect of this is the bike shed tint; .val is not really the right
> indicator given that the reference type is also a “value class”. I think
> we’re comfortable giving the “value” name to the whole family of
> identity-free classes, which means that .val needs a new name. Bonus
> points if the name connotes “having burst free of the constraints of
> reference-hood”: unbound, loose, exploded, compound value, etc. And also
> is pretty short.
FWIW, I have at no point been comfortable with that decision. My little
manifesto puts forth a strong meaning for "value" that bucket 2 has nothing
to do with. Bucket 2 objects don't even safely have "value semantics"
either (which are recursive).
The nomenclature I would like to see would center, somehow, around
B1: an identity class
B2: a non-identity class
B3: a non-identity class (that also gets a .val type)
(Thought experiment: if we had an annotation meaning "using the .val type
is not a great idea for this class and you should get a compile-time
warning if you do" .... would we really and I mean *really* even need
bucket 2 at all?)
One more: the .getClass() anomaly goes away.
>
> If we have
>
> mumble primitive mumble Complex { … }
>
> Complex.val c = …
>
> then what do we get when we ask c for its getClass? The physics again
> point us at returning Complex.ref.class, not Complex.val.class, but under
> the old scheme, where the val projection gets the good name, it would seem
> anomalous, since we ask a val for its class and get the ref mirror. But
> under the Kevin interpretation, we can say “well, the CLASS is Complex, so
> if you ask getClass(), you get Complex.class.“
>
First, I don't think we can really appeal to "well, the CLASS is..."
because people will know that there are two Class instances for that same
class, so it doesn't explain which one they'd get.
But actually I am increasingly convinced that this method shouldn't return
anything. If it must exist, it should just throw, because its purpose is to
get an object's dynamic type, and there is no object here, and no dynamic
type here. But even better: throwing would force the user to just write
whichever of `Complex.class` or `Complex.val.class` they actually mean, and
everyone would be better off for that.
--
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
More information about the valhalla-spec-observers
mailing list