<div dir="ltr"><div>All else being equal, the idea to use "inaccessible value type" over "value type doesn't exist" feels very good and simplifying, with the main problem that the syntax can't help but be gross.</div><div><br></div><div>So this makes it a local maximum, but I am persistently troubled by at least 2 broader things.</div><div><br></div><div>* It feels wrong to restrict access to the type only because of two very specific things we don't want people to do with the type. We don't want them to write `new TheType.val[size]`, and we don't want them to write `TheType.val someUnintializedField;`. Is there a third? And can we really not just prevent those specific things? It feels like baby/bathwater, especially since delayed initialization scenarios like those are already problematic in many ways as it is.</div><div><br></div><div>* I still am saddled with the deep feeling that ultimate victory here looks like "we don't need a val type, because by capturing the nullness bit and tearability info alone we will make <i>enough</i> usage patterns always-optimizable, and we can live with the downsides". To me the upsides of this simplification are enormous, so if we really must reject it, I may need some help understanding why. It's been stated that a non-null value type means something slightly different from a non-null reference type, but I'm not convinced of this; it's just that sometimes you have the technical ability to conjure a "default" instance and sometimes you don't, but nullness of the type means what it means either way.</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>* I think if we plan to go this way (.val), and then we one day have a nullable types feature, some things will then be permanently gross that I would hope we can avoid. For example, nullness *also* demands the concept of bidirectional projection of type variables, and for very overlapping reasons. This puts things in a super weird place.</div><div><br></div></blockquote><div><br></div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Jun 13, 2022 at 4:35 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>> wrote:<br></div><div dir="ltr" class="gmail_attr"><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div><font size="4"><font face="monospace">or, flipping the default: <br>
<br>
value class B3a { <br>
public class val { }<br>
}<br></font></font></div></blockquote><div><br></div><div>(assuming we go this way) A minor point: if we wanted, we could provide a way for this to also *name* the value type, but not allow anything outside java.lang to use it. The benefit would be if it means the word "Integer.val" doesn't have to exist at all, and overall the more we can demystifies how the whole int/Integer business works the more people can understand value types by comparison to them. The drawback is "no fair, why can't we do it too", but the answer to that is easy and compelling and it's easy to see why Integer and friends deserve an exception to it.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><font size="4"><font face="monospace">It's an orthogonal choice whether the default is "val is
private" and "val is public". </font></font></div></blockquote><div><br></div><div>"The default should always be to expose fewer capabilities to users and let them opt into what they actually need" -- earnestly, does anyone know a good counterexample to this rule?</div><div><br></div><div>An awkwardness of the default being private would just be that it's slightly confusing what is being accomplished by `class val { }` before you realize oh yeah, that's letting other classes in the package access it. Would that be justification for making the default be package visibility (or whatever it's really called), I'm not sure.</div><div><br></div><div><br></div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div>On 6/3/2022 3:14 PM, Brian Goetz wrote:<br>
</div>
<blockquote type="cite">
<font size="4"><font face="monospace">Continuing to shake this
tree. <br>
<br>
I'm glad we went through the exploration of "flattenable
B3.ref"; while I think we probably could address the
challenges of tearing across the null channel / data channels
boundary, I'm pretty willing to let this one go. Similarly
I'm glad we went through the "atomicity orthogonal to buckets"
exploration, and am ready to let that one go too. <br>
<br>
What I'm not willing to let go of us making atomicity explicit
in the model. Not only is piggybacking non-atomicity on
something like val-ness too subtle and surprising, but
non-atomicity seems like it is a property that the class
author needs to ask for. Flatness is an important benefit,
but only when it doesn't get in the way of safety. <br>
<br>
Recall that we have three different representation techniques:
<br>
<br>
- no-flat -- use a pointer<br>
- low-flat -- for sufficiently small (depending on size of
atomic instructions provided by the hardware) values, pack
multiple fields into a single, atomically accessed unit. <br>
- full-flat -- flatten the layout, access individual
individual fields directly, may allow tearing. <br>
<br>
The "low-flat" bucket got some attention recently when we
discovered that there are usable 128-bit atomics on Intel
(based on a recent revision of the chip spec), but this is not
a slam-dunk; it requires some serious compiler heroics to pack
multiple values into single accesses. But there may be
targets of opportunity here for single-field values (like
Optional) or final fields. And we can always fall back to
no-flat whenever the VM feels like it. <br>
<br>
One of the questions that has been raised is how similar
B3.ref is to B2, specifically with respect to atomicity.
We've gone back and forth on this. <br>
<br>
Having shaken the tree quite a bit, what feels like the low
energy state to me right now is:<br>
<br>
- The ref type of all on-identity classes are treated
uniformly; B3.ref and B2.ref are translated the same, treated
the same, have the same atomicity, the same nullity, etc. <br>
- The only difference across the spectrum of non-identity
classes is the treatment of the val type. For B2, this means
the val type is *illegal*; for B3, this means it is atomic;
for B3n, it is non-atomic (which in practice will mean more
flatness.) <br>
- (controversial) For all types, the ref type is the
default. This means that some current value-based classes can
migrate not only to B2, but to B3 or B3n. (And that we could
migrate to B2 today and further to B3 tomorrow.) <br>
<br>
While this is technically four flavors, I don't think it needs
to feel that complex. I'll pick some obviously silly
modifiers for exposition:<br>
<br>
- class B1 { }<br>
- zero-hostile value class B2 { }<br>
- value class B3 { }<br>
- tearing-happy value class B3n { }<br>
<br>
In other words: one new concept ("value class"), with two
sub-modifiers (zero-hostile, and tearing-happy) which affect
the behavior of the val type (forbidden for B2, loosened for
B3n.) <br>
<br>
For heap flattening, what this gets us is: <br>
<br>
- B1 -- no-flat<br>
- B2, B3.ref, B3n.ref -- low-flat atomic (with null channel)<br>
- B3 -- low-flat (atomic, no null channel)<br>
- B3n -- full-flat (non-atomic, no null channel)<br>
<br>
This is a slight departure from earlier tree-shakings with
respect to tearing. In particular, refs do not tear at all,
so programs that use all refs will never see tearing (but it
is still possible to get a torn value using .val and then box
that into a ref.)<br>
<br>
If you turn this around, the declaration-site decision tree
becomes:<br>
<br>
- Do I need identity (mutability, subclassing, aliasing)?
Then B1. <br>
- Are uninitialized values unacceptable? Then B2. <br>
- Am I willing to tolerate tearing to enable more
flattening? Then B3n. <br>
- Otherwise, B3. <br>
<br>
And the use-site decision tree becomes:<br>
<br>
- For B1, B2 -- no choices to make.<br>
- Do I need nullity? Then .ref<br>
- Do I need atomicity, and the class doesn't already provide
it? Then .ref<br>
- Otherwise, can use .val<br>
<br>
The main downside of making ref the default is that people
will grumble about having to say .val at the use site all the
time. And they will! And it does feel a little odd that you
have to opt into val-ness at both the declaration and use
sites. But it unlocks a lot of things (see Kevin's list for
more): <br>
<br>
- The default name is the safest version. <br>
- Every unadorned name works the same way; it's always a
reference type. You don't need to maintain a mental database
around "which kind of name is this".<br>
- Migration from B1 -> B2 -> B3 is possible. This is
huge (and more than we had hoped for when we started this
game.)<br>
<br>
(The one thing to still worry about is that while refs can't
tear, you can still observe a torn value through a ref, if
someone tore it and then boxed it. I don't see how we defend
against this, but the non-atomic label should be enough of a
warning.)<br>
<br>
<br>
</font></font><br>
<div>On 5/6/2022 10:04 AM, Brian Goetz
wrote:<br>
</div>
<blockquote type="cite">In
this model, (non-atomic B3).ref takes the place of (non-atomic
B2) in the stacking I've been discussing. Is that what you're
saying? <br>
<br>
class B1 { } // ref, identity, atomic <br>
value-based class B2 { } // ref, non-identity, atomic <br>
[ non-atomic ] value class B3 { } // ref or val, zero is
ok, both projections share atomicity <br>
<br>
If we go with ref-default, then this is a small leap from
yesterday's stacking, because "B3" and "B2" are both reference
types, so if you want a tearable, non-atomic reference type,
saying `non-atomic value class B3` and then just using B3 gets
you that. Then: <br>
<br>
- B2 is like B1, minus identity <br>
- B3 means "uninitialized values are OK, you get two types, a
zero-default and a non-default" <br>
- Non-atomicity is an extra property we can add to B3, to get
more flattening in exchange for less integrity <br>
- The use cases for non-atomic B2 are served by non-atomic B3
(when .ref is the default) <br>
<br>
I think this still has the properties I want; I can freely
choose the reasonable subsets of { identity, has-zero, nullable,
atomicity } that I want; the orthogonality of non-atomic across
buckets becomes orthogonality of non-atomic with nullity, and
the "B3.ref is just like B2" is shown to be the "false friend."
<br>
<br>
</blockquote>
<br>
</blockquote>
<br>
</div>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><div dir="ltr"><div style="line-height:1.5em;padding-top:10px;margin-top:10px;color:rgb(85,85,85);font-family:sans-serif"><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(213,15,37);padding-top:2px;margin-top:2px">Kevin Bourrillion |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(51,105,232);padding-top:2px;margin-top:2px"> Java Librarian |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(0,153,57);padding-top:2px;margin-top:2px"> Google, Inc. |</span><span style="border-width:2px 0px 0px;border-style:solid;border-color:rgb(238,178,17);padding-top:2px;margin-top:2px"> <a href="mailto:kevinb@google.com" target="_blank">kevinb@google.com</a></span></div></div></div></div></div></div></div></div>