Records -- current status

Brian Goetz brian.goetz at
Tue Mar 27 13:04:09 UTC 2018

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

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 

     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?

More information about the amber-spec-experts mailing list