Allowing inheritance with Records?

Brian Goetz brian.goetz at oracle.com
Fri Jan 5 14:27:39 UTC 2024



>  I hadn't even thought of the implications of valhalla. Even though it 
> is not an active topic of investigation, would you please share what 
> the uncomfortable details were? (I am asking out of sheer curiousity).

I'll throw out a few thoughts here on the condition that this not become 
an ongoing design party (or even a discussion); even just discussing 
"roads not yet taken" takes time away from the roads we *are* taking.

First, think about what constraints on extension we would be willing to 
place.  If record R extends record S, must the components of R contain 
exactly those of S?  (And, are we willing to identify them solely by 
name and type?  Do they have to be contiguous?  Do they have to be the 
first N?  Do they have to occur in the order they are declared in S?)  
If so, this limits the expressiveness of record extension, as well as 
being a pretty new and different kind of constraint than we've seen 
before.  And if not, we have a bigger problem; how do we know which of 
the components of R should be lowered to fields of R, and which exist 
solely to feed S?

The "easy" example looks something like:

     record S(int x) { }
     record R(int x, int y) extends S { }

Here, it seems obvious we should be able to factor the S components out 
of R.  And, with enough restrictions, we could automagically recognize 
that x belongs to S, not R.  You might think it is "just" a matter of 
requiring an explicit indication:

     record S(int x) { }
     record R(super int x, int y) extends S { }

which would say that "all the components marked super must be components 
of S, and all the rest are components of R."  But that doesn't solve 
most of the problems -- do the super components have to appear in the 
order they do in the S state description (and hence canonical 
constructor?)  Or are we again doing automatic matching by name and 
type, just slightly less magically?

With all these restrictions, the incremental expressiveness is pretty 
limited.  It would be much more useful to be able to _derive_ the 
components of S from the components of R, say through an explicit 
super() call in the canonical constuctor.  But then we have another 
problem - super calls are imperative.  So how do we know when and how to 
tell if a component of R has been "used up" in this way, and should be 
excluded from the normal treatment?

As you can see, what starts as a seemingly simple and achievable goal 
turns into a flurry of hyper-targeted micro-features (such as the 
`super` modifier on record components.)  While sometimes such 
micro-features are unavoidable, they're usually red flags that should be 
heeded.  The return on the major feature would have to be pretty big to 
justify the epicyclical features, and it's not really here.



More information about the amber-dev mailing list