New candidate JEP: 468: Derived Record Creation (Preview)

Attila Kelemen attila.kelemen85 at gmail.com
Tue Apr 23 20:55:36 UTC 2024


>
> So, a further thing to keep in mind is that currently, adding fields to
> records is not even source compatible to begin with.  For example, if we
> have
>
>     record Point(int x, int y) { }
>
> And a client uses it in a pattern match:
>
>     case Point(int x, int y):
>
> And then we add an `int z` component, the client will break.  (When we are
> able to declare deconstruction patterns, such a migration could include an
> XY pattern as well as constructor, but we are not there yet.)
>

To be honest, I didn't think of that scenario. So, thank you for making me
more alert to potential issues.


> I think your mail rests on the assumption that it should be fine to modify
> records willy-nilly and expect compatibility without a recompile-the-world,
> but I think this is a questionable assumption.  Records will likely have
> features that ordinary classes do not yet have access to for a while,
> making such changes risky.
>

While I would love to be "willy-nilly", in this issue that is not really my
concern. Because I'm just considering the following scenarios:

Scenario A: After changing the dependency, the dependent code is binary
compatible, and compiles fine, but does something else than before the
change.

Scenario B: After changing the dependency, the dependent code is binary
compatible, and fails to compile.

Scenario C: We had shadowing in the original code, and none of the above
happened.

Obviously we have to make an assumption that there is a naming ambiguity
one way or the other, otherwise there is no difference between shadowing or
compile time error.

I think I'm uncontroversial by stating that scenario A is far more damaging
than scenario B. And also that scenario C is considerably more likely than
the other events.

So, the question is if scenario C gains enough to allow for scenario A to
happen, which is the opinionated part of course. I would say that scenario
C almost always hurts readability. As someone might fail to notice the
ambiguity. Failing to notice it can be quite likely given that the
existence of the property shadowing the local variable (or field for that
matter) is not apparent (assuming that the declaration of the record is not
very close). So, someone reading such a code has a decent likelihood to
conclude that the variable is the local variable being shadowed, when in
fact it is the property of the record. While an IDE might color code this
(though I would guess they would both be considered local variable by the
IDE), but regardless what an IDE might do, I tend to read code in the
browser a lot (and I would assume I'm not alone), because I'm just lazy to
clone the repo (etc).

Now one could argue that shadowing would be beneficial in some cases for
lambda parameters, so why not here? But I think the situation is different.
The main reason why the lack of shadowing is sometimes bothersome in
lambda, is because there are quite a few methods taking an argument, and
then passing the same to the lambda (a'la `Map.computeIfAbsent`). That is,
the main thing is that it is beneficial, because we know that the variable
being shadowed has the same value as the lambda parameter. I don't think
this would be anywhere near as common for withers, because not only do we
need to have the property have the same value as the shadowed variable
(which I think is already unlikely), but we would need to use that value
within the withers. Not only this, but in case of lambda we always want to
use the lambda parameters over the local to save the cost of capturing the
local variable (for no reason). However, in case of withers, we have a
simple workaround for the rare case of unavoidable conflict: We can just
access that property via the source record (which should have the same
performance).

An additional argument for the preference of compile time error over
shadowing is that if shadowing is chosen for the final version, then there
is no going back (even if it laters turns out to be a mistake), while in
case compile time error is chosen, there is still an opportunity to
introduce shadowing later.

So, my conclusion is that compile time error is a far more preferable
behavior over shadowing.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240423/f7a35964/attachment.htm>


More information about the amber-dev mailing list