Re: The interfaces IdentityObject and ValueObject must die !

2022-03-22 Thread Kevin Bourrillion
On Wed, Jan 26, 2022 at 5:14 PM John Rose  wrote:

> A. I am aiming for new Object().getClass() == Object.class.
>
That you can do `new Object()` at all looks like the proverbial bathwater
here.

-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kev...@google.com


Re: The interfaces IdentityObject and ValueObject must die !

2022-01-27 Thread Dan Smith
On Jan 26, 2022, at 6:14 PM, John Rose 
mailto:john.r.r...@oracle.com>> wrote:


On 26 Jan 2022, at 16:55, Dan Smith wrote:

On Jan 26, 2022, at 4:55 PM, John Rose 
mailto:john.r.r...@oracle.com>> wrote:

Independently of that, for the specific case of Object

, having a query function Class.instanceKind

, which returns “NONE” for abstracts else “VALUE” or “IDENTITY”, would encode 
the same information we are looking at with those marker interfaces.

Right, so you're envisioning a move in which, rather than 'obj instanceof 
ValueObject', the dynamic test is 'obj.getClass().instanceKind() == VALUE'.

...

'Object.class.instanceKind()' must return NONE, just as Object.class must not 
implement either IdentityObject or ValueObject.

That last “must” is necessarily true, but the second-to-last “must” is not 
necessarily true. That’s my point here.

Okay, I understand—it's possible for library code to do whatever arbitrary 
things it wants, while 'instanceof' has specific, fixed behavior.

But... I don't really see how clients of this method would be comfortable with 
'Object.class.instanceKind()' and 'Runnable.class.instanceKind()' returning 
different things. They've both got to be 'NONE', it seems to me. What does 
'NONE' mean, if not "instances of this Class (and its subclasses) can be both 
value classes and identity classes"?

(I guess we could have two methods, one of which is called 
'directInstanceKind'. But how likely would users be to use the right one, 
depending on which question they were trying to ask? And wouldn't those users 
smart enough to ask the right question be okay just testing for Object.class as 
a special case?)



Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread forax
> From: "John Rose" 
> To: "daniel smith" 
> Cc: "Remi Forax" , "valhalla-spec-experts"
> 
> Sent: Thursday, January 27, 2022 2:18:03 AM
> Subject: Re: The interfaces IdentityObject and ValueObject must die !

> On 26 Jan 2022, at 16:36, Dan Smith wrote:

>> An instance of a class is also an instance of (and carries the properties of)
>> its superclasses. Value objects are instances of the class Object.

>> I can imagine a design in which we say that instances of Object may be either
>> identity or value objects, but direct instances of the class are always
>> identity objects. But this is not how we've handled the property anywhere 
>> else,
>> and it breaks some invariants. We've gotten where we are because it seemed 
>> less
>> disruptive to introduce a subclass of Object that can behave like a normal
>> identity class.
> And yet there is also a second way a class can be an instance; it can be 
> exactly
> an instance of C , when x.getClass()==C.class . That’s the condition which can
> be teased apart here, if we allow ourselves to use something other than marker
> interfaces. But marker interfaces (as I said) are committed to ignoring the
> “exactly an instance” condition, because they inherit.

YES ! 
being a value class (primitive class) is a runtime property, not something 
attached to a type and this is a separate concern from the flattenability which 
is either type hint (Q-type, L-type + Preload) or a type hint + a runtime check 
in case of arrays. 

Rémi 


Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread John Rose

On 26 Jan 2022, at 16:36, Dan Smith wrote:

An instance of a class is also an instance of (and carries the 
properties of) its superclasses. Value objects are instances of the 
class Object.


I can imagine a design in which we say that instances of Object may be 
either identity or value objects, but direct instances of the class 
are always identity objects. But this is not how we've handled the 
property anywhere else, and it breaks some invariants. We've gotten 
where we are because it seemed less disruptive to introduce a subclass 
of Object that can behave like a normal identity class.


And yet there is also a second way a class can be an instance; it can be 
*exactly an instance of C*, when `x.getClass()==C.class`.  That’s the 
condition which can be teased apart here, if we allow ourselves to use 
something other than marker interfaces.  But marker interfaces (as I 
said) are committed to ignoring the “exactly an instance” condition, 
because they inherit.

Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread Dan Smith


On Jan 26, 2022, at 4:36 PM, fo...@univ-mlv.fr wrote:

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

My point is that if you're holding an Object, there is nothing interesting 
about instances of primitive classes to distinguish them from instances of 
value classes. They are both value objects, with value object semantics.

Now, if you're holding an Object[] and you want to know whether the array is 
flat or not, that could be a useful property to detect. But a query like the 
following won't tell you:

PrimitiveObject.class.isAssignableFrom(arr.getClass().getComponentType());

Instead, the question you want to ask is something like:

arr.getClass().getComponentType().isPrimitive();

You're claiming that the IdentityObject vs. ValueObject and value class vs. 
primitive class distinctions should be treated the same, but what I'm 
illustrating here is that they are different kinds of properties. The first is 
a property of a class (or of all instances of a class), the second of a type 
(or a container with a type).

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.

Classes implement interfaces. Types have subtyping relationships *derived from* 
classes. The mechanism of encoding a property with an interface thus involves 
attaching that interface to a particular class.

So if you want QPoint <: PrimitiveObject, it must be the case that *class* 
Point implements PrimitiveObject. And then it will be the case also that LPoint 
<: PrimitiveObject.

(I don't want any of this. What I want is for there to be just one interface, 
ValueObject, that indicates identity-free semantics for a class/object, and I 
want class Point to implement that interface.)

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

  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.

You've lost me here.

I showed an example that uses universal generics. The type variable T ranges 
over both primitive and reference types, so may not be nullable. It is improper 
to write a null into a T[]. As a concession to compatibility, we want to treat 
this as a warning rather than an error, because plenty of code like this exists 
today.

Similarly, there may be code that ranges over both value objects and identity 
objects. An easy way to do this is with a variable of type Object. It is 
improper to synchronize on an Object that may be a value object. Again, as a 
concession to compatibility, we probably want to treat this as a warning.

public void m(Object obj) {
synchronized (obj) { // warning
...
}
}

These are both situations where being able to detect properties of the input 
could be useful to avoid runtime errors, but that doesn't mean the mechanism 
should be the same for both. (And I explained above why testing for 
PrimitiveObject[] wouldn't do what you want it to do.)

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.

You're envisioning a different model for types and classes than the one we have.

An instance of a class is also an instance of (and carries the properties of) 
its superclasses. Value objects are instances of the class Object.

I can imagine a design in which we say that instances of Object may be either 
identity or value objects, but direct instances of the class are always 
identity objects. But this is not how we've handled the property anywhere else, 
and it breaks some invariants. We've gotten where we are because it seemed less 
disruptive to introduce a subclass of Object that can behave like a normal 
identity class.

(I think we can explore this more, though, and note that I didn't say anything 
about interfaces above. It's orthogonal.)



Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread John Rose
[Sorry disregard last content-free message.  Still getting used to a new 
mail client.]


On 26 Jan 2022, at 7:42, Dan Smith wrote:

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.


That’s true, but stripping away the marker interfaces removes (one 
part of) the contract that a class, as a whole, must always accurately 
report whether *all its instances* have one or the other property, of 
having identity or having no identity.


As I said in an earlier meeting, there are sometimes reasons to give 
*the same class* both value instances and identity instances.  Yes this 
muddies the user model but it also helps us with compatibility moves 
that are required, however much they muddy the user mode.  (Actually all 
exact instances of `Object` will be identity objects.  I’m thinking of 
`Integer` which might want mostly values but some identity objects for 
backward compatibility.  Maybe.)


Independently of that, for the specific case of `Object`, having a query 
function `Class.instanceKind`, which returns “NONE” for abstracts 
else “VALUE” or “IDENTITY”, would encode the same information we 
are looking at with those marker interfaces.  But the contract for a 
method is *more flexible* than the contract of a marker interface.


In particular, `instanceKind` is not required to report the same thing 
for T and U when T<:U but marker interfaces are forced to be consistent 
across T<:U.  I think this is an advantage, precisely because it has 
more flexible structure, for the method rather than the marker 
interface.


If the marker interfaces also have little use as textual types (e.g., 
for bounds and method parameters) then I agree with Remi.  Ditch ‘em.

Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread John Rose



On 26 Jan 2022, at 7:42, Dan Smith wrote:

On Jan 26, 2022, at 2:18 AM, 
fo...@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.


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

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.


We could also make a special rule that says arrays of primitive types 
implement an interface PrimitiveArray. More generally, we've 
considered enhancements to arrays where there are different 
implementations provided by different classes. That seems plausible, 
but it's orthogonal to the IdentityObject/ValueObject feature.


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


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

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 {}
  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.


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.


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.


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.


Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread John Rose
On 26 Jan 2022, at 7:42, Dan Smith wrote:

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

Is this elephant also being followed around a crowd of blind men?  Too bad they 
can’t see its nice hat.

See also:  A bull walked into a china shop.  The owner said, “nice hat!”


Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread forax
> From: "daniel smith" 
> To: "Remi Forax" 
> Cc: "valhalla-spec-experts" 
> 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:fo...@univ-mlv.fr |
>> fo...@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:

>  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 {}
>>  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

Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread Dan Smith
On Jan 26, 2022, at 2:18 AM, fo...@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.

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

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.

We could also make a special rule that says arrays of primitive types implement 
an interface PrimitiveArray. More generally, we've considered enhancements to 
arrays where there are different implementations provided by different classes. 
That seems plausible, but it's orthogonal to the IdentityObject/ValueObject 
feature.

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

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

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 {}
  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.

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.

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.

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.



Re: The interfaces IdentityObject and ValueObject must die !

2022-01-26 Thread forax
- Original Message -
> From: "daniel smith" 
> To: "Remi Forax" 
> Cc: "valhalla-spec-experts" 
> Sent: Wednesday, January 26, 2022 6:20:07 AM
> Subject: Re: The interfaces IdentityObject and ValueObject must die !

>> On Jan 25, 2022, at 2:39 PM, Remi Forax  wrote:
>> 
>> I think we should revisit the idea of having the interfaces
>> IdentityObject/ValueObject.
>> 
>> They serve two purposes
>> 1/ documentation: explain the difference between an identity class and a 
>> value
>> class
> 
> And, in particular, describe the differences in runtime behavior.

parts of the difference in runtime behavior, you still have the issue where to 
document the difference between a value class and a primitive class.

> 
>> 2/ type restriction: can be used as type or bound of type parameter for
>> algorithms that only works with identity class
>> 
>> Sadly, our design as evolved but not those interfaces, they do not work well 
>> as
>> type restriction, because
>> the type is lost once an interface/j.l.Object is used and the cost of their
>> introduction is higher than previously though.
> 
> 
> Not sure there's a problem here. For example, if it's important to constrain
> type parameters to be identity classes, but there's already/also a need for an
> interface bound, nothing wrong with saying:
> 
> 
> 
> If the *use site* may have lost track of the types it would need to satisfy 
> the
> bound, then, sure, better to relax the bound. So, not useful in some use 
> cases,
> useful in others.
> 
> Other purposes for these interfaces:
> 
> 3/ dynamic tagging: support a runtime test to distinguish between value 
> objects
> and identity objects

aClass.isIdentityClass()/isValueClass()/isPrimitiveClass() does the same

> 
> 4/ subclass restriction: allow authors of abstract classes and interfaces to
> restrict implementations to only value classes or identity classes
> 
> 5/ identity-dependent abstract classes: implicitly identifying, as part of an
> abstract class's API, that the class requires/assumes identity subclasses

both these examples are sub cases of using type restriction, so they are really 
under 2/.

> 
>> 1/ documentation
>> 
>> - Those interface split the possible types in two groups
>>  but the spec split the types in 3, B1/B2/B3, thus they are not aligned 
>> anymore
>>  with the new design.
> 
> The identity class vs. value class distinction affects runtime semantics of
> class instances. Whether the class is declared as a value class or a primitive
> class, the instances get ValueObject semantics.
> 
> The primitive type vs. reference type distinction is a property of *variables*
> (and other type uses); the runtime semantics of class instances don't change
> between the two. Being a primitive class is statically interesting, because it
> means you can use it as a primitive type, but at runtime it's not really
> important.
> 
> 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.

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
  }

  public void swap(PrimitiveObject[] array, int i, int j) { // non tearable swap
synchronized(lock) {
  var tmp = array[i];
  array[i] = array[j];
  array[j] = tmp;
}
  }

  public class NullableArray ( // specialized 
generics
private final boolean[] nullables;  // use a side array to represent nulls
private final T[] array;
...

  }

Given that the model B1/B2/B3 has separated the runtime behaviors of the value 
class into 2 sub categories, I don't understand why the semantics of ==, 
synchronized() and weak reference of B2 is more important than the nullability 
and tearability of B3.


> 
>> - they will be noise in the future, for Valhalla, the separation between
>> identity object and value object
>>  may be important but from a POV of someone learning/discovering the language
>>  it's a corner case
>>  (like primitives are). This is even more true with the introduction of B2, 
>> you
>>  can use B2 for a long time without knowing what a value type is. So havin

Re: The interfaces IdentityObject and ValueObject must die !

2022-01-25 Thread Dan Smith
> On Jan 25, 2022, at 2:39 PM, Remi Forax  wrote:
> 
> I think we should revisit the idea of having the interfaces 
> IdentityObject/ValueObject.
> 
> They serve two purposes
> 1/ documentation: explain the difference between an identity class and a 
> value class

And, in particular, describe the differences in runtime behavior.

> 2/ type restriction: can be used as type or bound of type parameter for 
> algorithms that only works with identity class
> 
> Sadly, our design as evolved but not those interfaces, they do not work well 
> as type restriction, because
> the type is lost once an interface/j.l.Object is used and the cost of their 
> introduction is higher than previously though.


Not sure there's a problem here. For example, if it's important to constrain 
type parameters to be identity classes, but there's already/also a need for an 
interface bound, nothing wrong with saying:



If the *use site* may have lost track of the types it would need to satisfy the 
bound, then, sure, better to relax the bound. So, not useful in some use cases, 
useful in others.

Other purposes for these interfaces:

3/ dynamic tagging: support a runtime test to distinguish between value objects 
and identity objects

4/ subclass restriction: allow authors of abstract classes and interfaces to 
restrict implementations to only value classes or identity classes

5/ identity-dependent abstract classes: implicitly identifying, as part of an 
abstract class's API, that the class requires/assumes identity subclasses

> 1/ documentation
> 
> - Those interface split the possible types in two groups
>  but the spec split the types in 3, B1/B2/B3, thus they are not aligned 
> anymore with the new design.

The identity class vs. value class distinction affects runtime semantics of 
class instances. Whether the class is declared as a value class or a primitive 
class, the instances get ValueObject semantics.

The primitive type vs. reference type distinction is a property of *variables* 
(and other type uses); the runtime semantics of class instances don't change 
between the two. Being a primitive class is statically interesting, because it 
means you can use it as a primitive type, but at runtime it's not really 
important.

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

> - they will be noise in the future, for Valhalla, the separation between 
> identity object and value object
>  may be important but from a POV of someone learning/discovering the language 
> it's a corner case
>  (like primitives are). This is even more true with the introduction of B2, 
> you can use B2 for a long time without knowing what a value type is. So 
> having such interfaces front and center is too much.

I think it's notable that you get two different equality semantics—something 
you really ought to be aware of when working with a class. But it's a 
subjective call about how prominent that information should be.

> 2/ as type
> 
> - Being a value class or a primitive class is a runtime behavior not a 
> compile time behavior,
>  so representing them with special types (a type is a compile time construct) 
> will always be an approximation.
>  As a consequence, you can have an identity class (resp value class) typed 
> with a type which is not a subtype
>  of IdentityObject (resp ValueObject).
> 
>  This discrepancy is hard to grasp for beginners (in fact not only for 
> beginners) and make IdentityObject/ValueObject
>  useless because if a method takes an IdentityObject as parameter but the 
> type is an interface, users will have
>  to cast it into an IdentityObject which defeat the purpose of having such 
> interface.
> 
>  (This is the reason why ObjectOutputStream.writeObject() takes an Object as 
> parameter and not Serializable)

It is sometimes possible/useful to statically identify this runtime property. 
At other times, it's not. That's not an argument for never representing the 
property with static types.

And even if you never pay attention to the static type, it turns out that the 
dynamic tagging and inheritance capabilities of interfaces are useful features 
for explaining/validating runtime behavior.

> And the cost of introduction is high
> 
> - they are not source backward compatible
>  class A {}
>  class B {}
>  var list = List.of(new A(), new B());
>  List list2 = list;

How about List?

Yes, it's possible to disrupt inference, and touching *every class in 
existence* has the potential to be really disruptive. But we should validate 
that with some real-world code.

> - they are not binary backward compatible
>  new Object().getClass() != Object.class

This has nothing to do with the interfaces. This is based on the more general 
property that every class instance must belong to a class that is either an 
identity class or a value class. Interfaces are just the way we've chosen to 
encode that property. Abandoning the property entirely would be a