This seems perfectly sensible to me.

On 12/15/2023 3:22 PM, Dan Smith wrote:
When we last checked in on it, our story for superclasses/interfaces of value 
classes was as follows:

In general, classes/interfaces are "unconstrained" and support subclassing by both value 
and identity classes. The 'value' and 'identity' keywords act as a form of "sealing", 
restricting subclassing to only classes of the given type. Any class/interface that extends 
classes/interfaces with both keywords is in error.

As a special case, concrete classes and some abstract classes with certain 
properties are implicitly 'identity' classes.

This approach has a three-state categorization scheme expressed with two keywords 
('value', 'identity', and "neither"). This corresponds to two JVM flags 
(ACC_IDENTITY and ACC_VALUE).

I think it holds together fairly well, but it's also somewhat difficult to communicate, and not as 
intuitive as we might like. I find myself periodically reminding people (and sometimes myself): 
"don't forget that there are both value abstract classes and unconstrained abstract 
classes" or "unconstrained classes can't have mutable fields, those are only for identity 
classes".

As we've revised the construction mechanism, we've found ourselves searching for an explicit 
keyword for the third, unconstrained state: "value-capable" or "universal" or 
something. None of these keywords are very compelling, and I don't relish the job of trying to get 
people to adopt these fairly obscure, infrequently used terms.

-----

If we're willing to give up some fairly marginal fine-grained controls, this 
story can be simplified significantly.

Concrete classes: nothing new—a concrete class is an identity class by default, 
but may opt out of identity with the 'value' keyword. Concrete value classes 
are implicitly final and subject to a handful of extra constraints.

Abstract classes: an abstract class is also an identity class by default, but 
may use the 'value' modifier to indicate that it doesn't require identity. This 
not the same thing as saying its subclasses *must not* make use of identity, 
it's just an assertion about the abstract class itself (and its supers). Both 
value classes and identity classes may extend an abstract value class. These 
abstract classes are subject to the same constraints as concrete value classes.

Interfaces: all interfaces can be implemented by value classes and identity 
classes. Full stop. If you have a particular need to limit the kinds of 
implementing classes, you can use a sealed interface and provide the 
implementation yourself, or you can make it an informal part of the contract. 
But, like access control, synchronization, and other fine-grained, 
implementation-oriented features, identity is not something interfaces can 
explicitly control.

This approach has a two-state categorization scheme for classes expressed with 
one keyword ('value' and identity). This corresponds to one JVM flag 
(ACC_IDENTITY, for {reasons}). Interfaces have only one state.

What do we lose?
- You can't force an abstract class/interface to be implemented by *only* value 
classes
- You can't force an interface to be implemented by *only* identity classes
- You can't declare an abstract class or interface whose type will refuse to 
support the 'synchronized' keyword

I don't think any of these are enough to justify the extra costs of 3 states or 
an 'identity' keyword. (For awhile, I was considering keeping around the 
'identity' keyword, just for the purpose of interfaces. But, eh, nobody is 
going to use it.)

Reply via email to