> Extension

Do I understand it right that you are proposing that Records cannot inherit normal classes, while abstract records can be inherited by normal classes?

More restrictive than that:
 - records can extend abstract records, or Object (really, AbstractRecord)
 - that's it.

There are two primary drivers why non-abstract records shouldn't be extended by something else (record or not):

 - Identity anomalies.  If S extends R, where R is a record, then ctor composed-with dtor is not an identity on R.  That means if someone does:

    case R(R_ARGS) -> new R(R_ARGS)

they may think they are cloning, but in fact they are decapitating. (Even if S has no additional state over R, it still has typestate that is discarded.)

 - Equality anomalies.  If S can extend R, and S has state that wants to participate in equals/hashCode, and therefore wants to override equals, this may violate the symmetry or transitivity of equals.

The current notion of abstract records avoids both of these because it has no equality (abstract records reabstract equals), and has no constructors, so ctor \compose dtor is meaningless on abstract records.

Is a decision to make class be a record a binding decision over long term?

We intend that there should be a source- and binary- compatible refactoring between a record and an equivalent class.

The class to record direction is the one that is immediately interesting, since people have source bases full of classes they'd like to turn into records.  This is OK as long as the members the class already has (constructor, equals) conform to the semantics of their record equivalents, and the author is OK with the class being final and transparent.

The other direction is likely to be less common, but also important; it is analogous to refactoring an enum to a class that uses the type-safe enum pattern, which happens once in a while when you hit the limits of what you can do with an enum/record.  But given that the goal all along is to have records be "just macros for corresponding classes", this should be doable.

Where the decision is binding is when you take on a specific class signature:

    record Point(int x, int y);

If you want to add a `z` to this, you're venturing onto the ice. Existing code may assume the existence of an XY constructor or deconstructor; we can probably handle this via adding additional explicit ctor/dtors.  But existing code may also depend on the behavioral assumption that equality of points is based on x and y. So the record <--> class refactoring is practical, but the record <--> different record refactoring is dicier.  (That said, the sweet spot for records is when they are used within a maintenance boundary, where you can find all the uses.  So this might be OK too.)

Given that records cannot inherit from normal classes, but normal classes can inherit from records AND names are important part of source level compatibility, I'm torn on abstract records.

With the more restricted understanding, that normal classes cannot extend records, does this change your position?


Reply via email to