Records -- current status

Brian Goetz brian.goetz at
Fri Mar 16 22:09:57 UTC 2018

On 3/16/2018 5:59 PM, Kevin Bourrillion wrote:
> On Fri, Mar 16, 2018 at 2:28 PM, Brian Goetz <brian.goetz at 
> <mailto:brian.goetz at>> wrote:
>     But also, there are times when matching against the abstract type
>     makes sense too.  For example, if you want to traverse the tree
>     and perform structural operations (say, detect if a tree contains
>     a reference to the variable "x"), matching on abstract records is
>     pretty useful:
>         boolean containsVar(Node node, String name) {
>             return switch (node) {
>                 case VarNode(String s) -> s.equals(name);
>                 case BinOpNode(var left, var right) ->
>     containsVar(left, name) || containsVar(right, name);
>                 default -> false;
>             }
>         }
> Am I correct that if BinOpNode is an interface there will be a way for 
> it to specify how it destructures so that it can get this effect also 
> - and it's just that records are neat because they know how to 
> destructure for free?

Destructuring for free is important, but it's not just destructuring -- 
it's all the stuff.  It means that the fields and accessors (and 
therefore, any methods derivable from that state that is common to all 
subtypes) get pulled into the abstract record too.  Remember, records 
can have behavior that is derived from their state.  So if there is any 
behavior that is natural on a BinaryOpNode, to put it there, it needs to 
have its state (or at least state accessors) there.

> We want people to be solid on the fact that two records with all the 
> same field values are always equals(), and then they may apply that 
> view to an abstract record type where it doesn't hold true.

equals() on abstract records is abstract; only the concrete record gets 
to declare equals.

> Pretend we already had non-final.  Does that change your inclination?
> I don't think so? The reversed default behavior feels like arbitrary 
> difference from regular fields (again, I do /want/ to encourage 
> finalness of record fields...). Would we permit the "not final" 
> keyword on interface fields too?

Hadn't thought about that, but, assuming we didn't think that was a bad 
idea, yes, we surely could do that.  (In other words; interface fields 
should be final because we think its dumb for them to be mutable, not 
because we don't have a way to spell it.)

I am usually wary of "let's flip the default on this new thing because 
we can" arguments.  This seems one of the few places where we could 
really get away with it, so I want to consider it seriously.  If we 
think its a bad idea, I'm OK with ultimately saying "nah, its like 
classes."  But I don't want to skip over that deliberation, and 
certainly not for a silly reason like "but we can't spell it"!

> OK, but do  you have an opinion on whether records should 
> automatically acquire a clone() implementation?
> As much as possible we'll encourage all-final, array-free records that 
> have no need to be cloned, but some number of records will go against 
> that, and I guess it's better that they have clone() than that they 
> don't. But my concern is: What does it do -- deep-clone arrays but 
> shallow-clone everything else? Sounds problematic no matter which way 
> you decide it.
Yes, that's the question.  One possibility is just to always clone 
shallowly; this is not as dumb as it sounds, since the fields are 
already exposed for read, and therefore any deep mutability is already 
flapping in the wind.

The primary value of cloning would probably be taking snapshots of 
mutable things like statistics-gathering records, which are a related 
bag of mutable values, and sometimes you want a consistent snapshot.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list