Getters vs. public final fields in records
Brian Goetz
brian.goetz at oracle.com
Sat Aug 13 16:23:27 UTC 2022
> There are actually two conditions in the definition of an
> embedding-projection pair (I am using “o” for function composition below);
>
> 1) p o e = id
> 2) e o p <= id
>
> Where <= means, roughly speaking, “less defined than”; it is equality,
> but allowing the left-hand side to be partial, i.e. non-terminating on
> some inputs
A good intuition for <= here is "approximates" or "has less information
than." The bottom value (which is used to describe
nonterminating/throwing operations) is a uniformly terrible (zero
information) approximation for everything. But e-p pairs are not just
about partiality; they are also about information loss. (The discrete
partial ordering, which allows partiality but no other approximation, is
a common but less interesting case.) This model was chosen to also
support _normalization_ such as defensive copies (this is a form of
throwing away information, namely identity), rounding, truncating (e.g.,
float to int), clamping ("if (x > n) x = n"), replacing invalid
components with valid ones (e.g., replacing (_, 0) with (0, 1) in a
Rational class), etc.
> Following your “translation” into Java (and ignoring several
> technicalities that make it problematic…), taking the constructor to
> be e and the destructors to be p,
Except you've got your mappings the wrong way around. The cartesian
product space of the components is the "bigger" space; the constructor
_projects_ from the unrestricted cartesian product space into the
potentially-more-restricted record space, potentially losing
information. The deconstructor (or vector of accessors) embeds back from
the restricted record space to the unrestricted space, and does not lose
information. (This is easy to get backwards; I often have to work it
out from scratch when I get confused, and I have even mistyped "embed"
for "project" once in this mail already.)
> 1) means:
>
> (new R(a1, …, an)).p1().equals(a1)
> … and …
> (new R(a1, …, an)).pn().equals(an)
>
> 2) means (roughly)
>
> new R(r.p1(), …, r.pn
> <https://urldefense.com/v3/__http://r.pn__;!!ACWV5N9M2RV99hQ!JQG3adQp8m1iHE_imVAw7SlzgNphGS5ZRKlDzRAUrGs3XsCgwOszFt1dkJblhuW9ishVPHZsTkA_d8xT$>()).equals(r)
> *OR* new R(r.p1(), … r.pn
> <https://urldefense.com/v3/__http://r.pn__;!!ACWV5N9M2RV99hQ!JQG3adQp8m1iHE_imVAw7SlzgNphGS5ZRKlDzRAUrGs3XsCgwOszFt1dkJblhuW9ishVPHZsTkA_d8xT$>())
> fails to terminate
Since you got your e/p backwards, you've got = / <= backwards too.
> 2) is close to the condition you claimed, but look at 1) — it *forces*
> the accessors to return the inputs to the constructor *according to
> the existing equals() defined on the inputs* — so, when equals() on
> the inputs is reference equality, the accessor is forced to return the
> exact same reference.
When you fix it, you get
(new R(a1 .. an).p1() <= a1
which says you get a potentially normalized version of a1 out, as expected.
(I actually don't think you get as strong a component-by-component
relation as the (1) and (2) relations you claim from the spec we have,
but since I would have _liked_ to have gotten that, I'm not arguing with
them.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20220813/820ab003/attachment-0001.htm>
More information about the amber-dev
mailing list