The interfaces IdentityObject and ValueObject must die !

forax at univ-mlv.fr forax at univ-mlv.fr
Wed Jan 26 23:36:09 UTC 2022


> From: "daniel smith" <daniel.smith at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "valhalla-spec-experts" <valhalla-spec-experts at openjdk.java.net>
> Sent: Wednesday, January 26, 2022 4:42:30 PM
> Subject: Re: The interfaces IdentityObject and ValueObject must die !

>> On Jan 26, 2022, at 2:18 AM, [ mailto:forax at univ-mlv.fr |
>> forax at univ-mlv.fr ] wrote:

>>> In other words: I don't see a use case for distinguishing between primitive and
>>> value classes with different interfaces.

>> Primitive classes does not allow nulls and are tearable, following your logic,
>> there should be a subclass of ValueObject named PrimitiveObject that reflects
>> that semantics.

> But this isn't a property of the *class*, it's a property of the *type*, as used
> at a particular use site. If you want to know whether an array is flattened,
> the class of the component can't tell you.

The semantics B1/B2/B3 is a property of the class, an instance of a value class stored in an Object is still an instance of a value class, same for primtive classes. 

Being flattenable or not, is an orthogonal concern. Being flattenable for a field or a local variable/parameter it's a property of the descriptor, but for arrays it's a property of the array class because arrays are covariant in Java. 

For arrays, a code like this should work 
Object[] o = new QComplex;[3]; 
o[1] = QComplex;.new(...); 

>> This is especially useful when you have an array of PrimitiveObject, you know
>> that a storing null in an array of PrimitiveObject will always generate a NPE
>> at runtime and that you may have to use either the volatile semantics or a lock
>> when you read/write values from/to the array of PrimitiveObject.

>> For examples,
>> public void m(PrimitiveObject[] array, int index) {
>> array[index] = null; // can be a compile time error
>> }

> If we said

> primitive class Point implements PrimitiveObject

> then it would be the case that

> Point.ref[] <: PrimitiveObject[]

> and so PrimitiveObject[] wouldn't mean what you want it to mean.

> We could make a special rule that says primitive types are subtypes of a special
> interface, even though their class does not implement that interface. But that
> doesn't really work, either—primitive types are monomorphic. If you've got a
> variable with an interface type, you've got a reference.

I don't understand why you want Point.ref/LPoint; to be a PrimitiveObject and not a ValueObject. 
We have QPoint;[] <: LPoint;[] so LPoint; implements ValueObject and QPoint; implements PrimitiveObject. 

Anyway, it shows that using interfaces to represent properties of a class (behavior of ==, synchronized, nullability, tearability) is far from obvious. 

> Meanwhile, I'd suggest writing the method like this, using universal generics:

> <T> public void m(T[] array, int index) {
> array[index] = null; // null warning
> }

If you are Okay with code that can raise a NPE, why are you not Okay with code that can raise an IllegalMonitorStateException ? 
Or said differently why ValueObject and not PrimitiveObject. 

I'm not advocating for having 3 interfaces, because i think of the ratio cost/benefit of such interfaces is poor. But having 2 interfaces is definitively weird since we have updated the model to B1/B2/B3. 

>> An impossible type, it's a type that can be declared but no class will ever
>> match.

>> Examples of impossible types, at declaration site
>> interface I extends ValueObject {}
>> interface J extends IdentityObject {}
>> <T extends I & J> void foo() { }
> It would definitely be illegal to declare a class that extends I and J. Our
> rules about well-formedness for bounds have always been sketchy, but
> potentially that would be a malformed type variable.

another cost of introducing those interfaces, the JLS has to be updated to take care of the invalid bounds 

>>> Abandoning the property entirely would be a bigger deal.

>> If we do not use interfaces, the runtime class of java.lang.Object can be
>> Object, being an identity class or not is a just a bit in the reified class,
>> not a compile time property, there is contamination by inheritance.
> Object can't be an identity class, at compile time or run time, because some
> subclasses of Object are value classes.

Object the type is not an identity class, but Object the class (the Object in "new Object()") is an identity class. 

> What you'd need is a property of individual *objects*, not represented at all
> with the class. Theoretically possible, but like I said, a pretty big
> disruption to our current model.

nope, we need to decouple the notion of type from the notion of being an identity/value/primitive class because it's a property of the class not a property of the type. 

>> For me, it's like opening the door of your house to an elephant because it has a
>> nice hat and saying you will fix that with scotch-tape each time it touches
>> something.

> Ha. This sounds like maybe there's a French idiom involved, but anyway we should
> try to get John to add this to his repertoire of analogies.

"Like a bull in a china shop" is "comme un éléphant dans un magazin de porcelaine" in French. 

Rémi 


More information about the valhalla-spec-observers mailing list