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