Records -- current status

Kevin Bourrillion kevinb at
Fri Mar 16 22:36:53 UTC 2018

On Fri, Mar 16, 2018 at 3:09 PM, Brian Goetz <brian.goetz at> wrote:

On 3/16/2018 5:59 PM, Kevin Bourrillion wrote:
> On Fri, Mar 16, 2018 at 2:28 PM, Brian Goetz <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.

Sure, I was assuming you have to put the accessors explicitly on the
BinOpNode interface, which is a bit more cumbersome than getting to use
record syntax, but only a bit. What else will go wrong? Note: I've been
curious what explicit destructuring is expected to look like.

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.

*Eppur si muove...* what I mean is, nevertheless equals() is usable and
will sometimes return a `false` that may be surprising. Yeah, it's not
fundamentally different from the `Collection` problem, and yeah, I do think
we can probably live with it; it's just not "free".

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

Hadn't thought about it either. I decided to think about it just now and,
oh, it's a completely *disgusting* idea, because it'd still be implicitly
static, mutable statics are The Worst, plus you could modify it from
default methods without even realizing it's not instance state.

So add all this up and we have *three* kind of finalness for fields:

- by default mutable, but you can change it
- by default final, and you can't change it
- (and now) by default final, but you can change it

This seems like quite a bad situation to me.

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

Okay, I guess that's the right move because you kinda want
`record.clone().equals(record)`. But a user's assumption that
`record.clone()` would deep-clone the array might be the entire reason
they're using clone() at all. Oh well, it's not like we'd be making arrays
awful for the first time.

Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list