[External] : Re: New candidate JEP: 401: Primitive Objects (Preview)

Brian Goetz brian.goetz at oracle.com
Tue Apr 20 18:46:57 UTC 2021



> Let me add that I don't think that primitive classes will be a 
> frequent case for a standard developer. 98% of Java developers will 
> probably never be tempted to ever write their own primitive classes. I 
> see this as a feature only for some rather low-level types, like 
> Optional or some of the java.util.time classes. I can hardly imagine 
> much more, even including all the standard frameworks - only some 
> mathematical libraries and some internal classes in collection 
> frameworks will make use of it. Or the disruptor framework. But there 
> the benefit is huge!

I hope this will be true, but I'm also aware that developers don't 
always do what you expect them to.  Developers can be inordinately 
attracted to "performance" features; if some blog author says "primitive 
classes are faster" (which surely will happen), that may motivate a lot 
of developers using them, even when they shouldn't, at least for the 
first few years until more universally-accepted "best practices" emerge.

> But at the end, it's a matter of opinion, and I understand your 
> concerns against such an "awkward look" for primitive class names.

FWIW, in the early days of OO languages, we saw a lot of conventions 
like "interface names begin with `I`" and "field names begin with `f_`.  
I'm glad that burned itself out before Java came along.

Also FWIW, one of the considerations when choosing this was: in the 
difference between "Point by value" and "Point by reference", which is 
more important to readers, Point-ness, or val/ref-ness?  We deemed it to 
be the data type, which is why that comes first and the carrier marking, 
if any, comes last.


>
>>
>> Fourth, while this reduces the chance that a user will mistake a 
>> primitive class instance for a reference class instance, the cost of 
>> this is that APIs become, from the perspective of many users, 
>> gratuitously inconsistent.  Having some classes called "account" and 
>> others called "AccountGroup" will also be a persistent irritant.
>
> You will find examples where this is true, but I don't buy this one. 
> ;) I can't imagine why someone would want to declare the domain class 
> 'Account' as primitive? Especially when other classes in the same 
> model are not. If someone is mixing primitive and reference types in 
> the same model together, then he or she is doing something very wrong. 
> I would be curious if there is a useful real life example for such a 
> use case, but I can't find one.

I think it goes back to the "performance is (over)important" thing. I 
can easily imagine developers will declare the classes that can be 
primitives, as primitives, only using identity where they need to. 
Whether this is good or bad is another question, but I suspect it will 
happen.  (We see this today with enums and records; where people can use 
the more restricted form, they do, because of the benefits.  We see this 
as normal, in part because we've set up enums and records as "just being 
classes".)

> And even if they do so: It's not much better with the current 
> proposal. If you ask the account for its groups, you get instances of 
> AccountGroup, but if you ask the group for its accounts, you get 
> instances of Account.ref. That's also irritant.

No, that's not right.  If I have:

     Point.val p1 = ...
     Point.ref p2 = p1

then `p1 == p2` and `p1.getClass() == p2.getClass()`.  The ref/val is 
just the "envelope"; the enclosed object is the same either way.

>
>>
>>> * The suffixes .ref and .val don't fit into our concept of class 
>>> names, they look ugly and can easily be mixed up
>>
>> I'm really glad you brought this up, because it's a common 
>> misperception.
>>
>> [Some good context here]
>
> Thanks for the insight. And to be honest, I have no concerns against 
> the .ref suffix; one would rarely explicitly use the reference type 
> when the value type could also be used, and if so, it's perhaps a good 
> idea to mark this more prominent. Also primitive types will rarely be 
> used in standard collections, I assume. So an argument for the suffix 
> style.
>
> what I find sad is that we're in the need for the .val suffix. But 
> that better explained in the next section.

Yes, I am sad too!  But realistically, this _will_ be rare, because 
relatively few classes will get migrated.  (Probably 90% of the pain 
will be with Optional.)  But there's a principle here, too: make the 
migrated classes carry the cost of migration, so that all-new code 
doesn't have to.  In the all-new-code world, you never say .val except 
in very strange circumstances.

> So there are two distinct naming schemas for the same kind of 
> reference/value pairs. The only difference is that the one class was 
> introduced before the JEP, and the second one after that.

Close -- the difference is that the class was _migrated to a primitive 
class_.  One can imagine classes being born identity classes after this 
JEP, and still migrating.  Not all classes will migrate on a special 
Migration Day.  But yes, your point stands, if the class was born 
differently, it gets the reversed naming convention.  Still, most of the 
time, in either case, you can just say Optional, and the system will do 
something reasonable.  Only in the conjunction of (a) migrated types and 
(b) performance-obsession will you tune the envelope of each declaration 
explicitly.

> If there wouldn't be a 'float' primitive yet, we would call the class 
> Float and the boxed type Float.ref. Or if it would've been introduced 
> a bit earlier, it would have been Float.val and Float. But it's not, 
> the primitive name if float and the boxed type Float, just as in my 
> proposal.
>
> What I read is that you plan to define aliases for, e.g., float that 
> refers to Float.val. This wouldn't be necessary if the naming scheme 
> would already cover the existing pattern of primitive types. Except 
> int and char which are falling out a bit.
>
> So the idea is avoiding three different naming schemes for the same 
> concept.
>
> And if someone want to invent a type for imaginary numbers, they can 
> call the class imaginary, the boxed type is Imaginary, and it fits 
> very well into the existing primitive types. It just feels similar.

This is the subject of JEP 402.  Currently we have eight primitive / box 
pairs (of which two have the unfortunate characteristic that the name is 
not even the same modulo case of the first character, Integer and 
Character.)  The plan is to:

  - migrate the box classes to primitive classes;
  - define int.ref to mean Integer;
  - define Integer.val to mean int.

This way, the only special thing is that these legacy classes have 
ad-hoc aliases; int and Integer.val are the same type, and int.ref and 
Integer are the same type.  Clearly we can't get rid of these names, 
they're too pervasive, but we can tame them a bit.

Cheers,
-Brian




More information about the valhalla-dev mailing list