I think this is getting out of hand.

Kevin presented a carefully thought out argument — which I am sure he spent 
many hours on before sending — about why we might have (yet again) gotten the 
defaults wrong.  I think we owe it to Kevin (and all the Java developers) to 
think as carefully about his observations as he did before he sent them; 
blasting back with knee-jerk “but this is the answer” is not helpful.  We’re 
not finishing designing the user-facing part of this, and we dare not close our 
minds to the points Kevin has raised, whether or not the eventual answer goes 
that way or not.





On Apr 25, 2022, at 4:13 PM, Remi Forax 
<fo...@univ-mlv.fr<mailto:fo...@univ-mlv.fr>> wrote:



________________________________
From: "Kevin Bourrillion" <kev...@google.com<mailto:kev...@google.com>>
To: "valhalla-spec-experts" 
<valhalla-spec-experts@openjdk.java.net<mailto:valhalla-spec-experts@openjdk.java.net>>
Sent: Monday, April 25, 2022 4:52:50 AM
Subject: [External] Foo / Foo.ref is a backward default; should be Foo.val / Foo
Hi,

The current plan for `primitive class Foo` -- to call the value type `Foo` and 
the reference type `Foo.ref` -- is causing a few problems that I think are 
unnecessary. I've felt for a while now that we are favoring the wrong default. 
We should let `Foo` be the reference type and require `Foo.val` (precise syntax 
aside) for the value type.
I started to list reasons and came up with more than expected.

If ref is the default for B3 then B3 is a worst B2, it's like saying let's 
transform all long to Long.



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.

Users should use B2 by default, you did agree about that.
if users want B3 we should give them B3, asking for B3.val is a kind of double 
opt-in.



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.

.ref should be rare in the end, it's mostly a stopgap measure because we do not 
have universal generics.
Once we have universal generics, Foo.val make even less sense.



3. This change would permit compatible migration of an id-less to primitive 
class. It's a no-op, and use sites are free to migrate to the value type if and 
when ready. And if they already expose the type in their API, they are free to 
weigh the costs/benefits of foisting an incompatible change onto *their* users. 
They have facilities like method deprecation to do it with. In the current 
plan, this all seems impossible; you would have to fix all your problematic 
call sites *atomically* with migrating the class.

B3 requires that the default value that makes sense and that bypassing the 
constructor is fine (because you can construct any values by "merging" existing 
values).
Maybe we should disallow users to even write constructors to avoid to get them 
false hope.

Anyway, those constraints mean that you will not be able to refactor most of 
the existing classes to a primitive classes because you are loosing 
encapsulation by doing that.



4. It's much (much) easier on the mental model because *every (id-less) class 
works in the exact same way*. Some just *also* give you something extra, that's 
all. This pulls no rugs out from under anyone, which is very very good.

No , B2 and B3 are different runtime models, even if B3 is ref by default.
The idea of B3 being ref by default is infact dangerous exactly for the reason 
you explain, it looks like the two models are the same.
The problem is that they are not.



5. The two kinds of types have always been easily distinguishable to date. The 
current plan would change that. But they have important differences 
(nullability vs. the default value chief among them) just as Long and long do, 
and users will need to distinguish them. For example you can spot the redundant 
check easily in `Foo.val foo = ...; / requireNonNull(foo);`.

You want a use site way to see if a type is a B3 as opposed to B1 and B2 that 
are both nullable.
It's something that can be discussed separately.


6. It's very nice when the *new syntax* corresponds directly to the *new 
thing*. That is, until a casual developer *sees* `.val` for the first time, 
they won't have to worry about it.

But it's not true, compare
  Complex.val c = new Complex(1, 2);
and
  Complex c = new Complex(1, 2);



7. John seemed to like my last fruit analogy, so consider these two equivalent 
fruit stand signs:

a) "for $1, get one apple OR one orange . . . with every orange purchased you 
must also take a free apple"
b) "apples $1 . . . optional free orange with each purchase"

Enough said I think :-)

8. The predefined primitives would need less magic. `int` simply acts like a 
type alias for `Integer.val`, simple as that. This actually shows that the 
whole feature will be easier to learn because it works very nearly how people 
already know primitives to work. Contrast with: we hack it so that what would 
normally be called `Integer` gets called `int` and what normally gets called 
`Integer.ref` or maybe `int.ref` gets called `Integer` ... that is much 
stranger.

int can not be an alias of Integer.val, it's a little more complex than that,
by example
  int.class.getName().equals("int")

but at the same time,
  we want ArrayList<int> to be ArrayList<Qjava/lang/Integer>.



What are the opposing arguments?

The runtime model of B2 and B3 are not the same. Defaulting B3 to B3.ref make 
things dangerous because users will have trouble to see how B2 and B3 are 
different at runtime.

Rémi

Reply via email to