Quick observation: the default value of `String!` is less dangerous to
allow free, because its "null", and the user can already freely make
nulls. Whereas the default value of `Instant` is not something the
user can get their hands on, if they can't observe fields of type
`Instant.val` during initialization. So the problem may be less severe
for B1.
On 6/16/2022 3:16 PM, Kevin Bourrillion wrote:
On Wed, Jun 15, 2022 at 12:01 PM Brian Goetz <[email protected]>
wrote:
OK, let's say for sake of argument that "well, that's what you
opted into." Non-atomic means no one can count on cross-field
integrity; don't select non-atomic if you have invariants to
protect. OK fine. And let's flip over to what T! means.
Let's say that T! is a restriction type; it can take on the values
of T, except for those prohibited by the restriction "t != null".
So, what is the default value of `String!`?
I'd like to slightly rephrase your question to "What do we do when we
need a default value /for/ `String!`", because I don't like the
framing that suggests that a default value is an inherent property /of
a type/ itself. (I've no idea what type theorists would say.)
And we were talking about bucket 2, a "value class with no good
default" so I'll substitute `Instant!` instead of `String!` for most
of this.
So, what to do when we need a default value for `Instant!`? I guess
"just blow up" is a non-option because every field has to start off
somewhere. So I guess we have to answer "it's `fromEpochMilli(0)`
because it can't be anything else, but we're going to do what we can
to prevent its users from depending on that fact." Definitely, making
an explicit value type that's nonpublic is a way to do just that. Is
there a more surgical way to do it?
One easy way to get surgical is to have OpenJDK just stop worrying
about the bad-default-values problem, and let aftermarket static
analyzers like ours take up that mantle. We can have an annotation to
mark classes like Instant, and we can issue warnings when we see bogus
usages (some of which we warn on anyway). In fact, if you do exactly
what you're planning (so flip back from Instant! to Instant.val, and
give the val type an access modifier), I guess we might end up doing
this in Error Prone anyway, so that people can make their value types
public safely. That would feel actually totally fine to me. And in the
`Instant!` world, there's not much to hang a modifier on, but we
wouldn't care if we were doing this checking anyway.
You don't need to explain that "we'd rather release language features
that /don't/ need aftermarket tools to use safely", I know it. But it
is just a platitude really. I think that any language design
expressive enough to users do good things will inevitably be
expressive enough to let them do bad things too; static analysis
always has a crucial role to play imho. And of course it is always the
trio of language/libraries/tools together that drives the user's
ultimate experience.
(Now changing back to `String`, a bucket-1 class, I've been expecting
it will be much longer before we'd roll out !/? to those types, but
when we do, I think your particular question comes out better. "What's
the default for `String!` when we absolutely must have one?" Well,
when we must we must, so we must commit null pollution. We try to
issue enough of the right warnings to live with the fallout. If we
ever make a transition like this, we have to level expectations; I'm
convinced null pollution will be a part of all of our lives, more so
than heap pollution of the generics kind ever was, but I'm also still
optimistic that it will still be worth it. You could say that today we
live with 100% null pollution...)
For locals, it's pretty clear we don't have to answer, because
locals cannot be accessed unless they are DA at the point of
access. But for fields, we have a problem -- and for arrays, a
bigger one. We can try to require that fields have initializers,
but there are all sorts of situations in which a field can be read
before its initializer runs.
... which situations already lead to bad behavior / puzzlers as it is.
We might miss a warning we'd rather have been able to give, but life
goes on?
And arrays are much worse.
Arrays in general, or just the one single construction path `new
TheType![size]` (or `new TheType.val[size]`)? I would just say please
give us new Arrays methods or syntax that create and fill at once, and
we'll get busy clamping down on everything else.
On 6/15/2022 2:10 PM, Kevin Bourrillion wrote:
On Wed, Jun 15, 2022 at 10:51 AM Brian Goetz
<[email protected]> wrote:
- If we spelled .val as !, then switching from P[] to P![]
not only prohibits null elements, but changes the layout and
_introduces tearing_. Hiding tearability behind "non-null"
is likely to be a lifetime subscription to Astonishment
Digest, since 99.9999 out of 100 Java developers will not be
able to say "non-null, oh, that also means I sacrifice
atomicity."
Well, that's what you opted into when you... wait a minute...
The link you probably want to attack is this last one, where
you are likely to say "well, that's what you opted into when
you said `non-atomic`; you just happen to get atomicity for
free with references, but that's a bonus."
Your Kevin's Brain Emulator has gotten pretty decent over time...
check whether the next things it said were these (probably so):
A good clean Basic Conceptual Model For Novices is allowed to
have a bunch of asterisks, of the form "well, in $circumstance,
this will be revealed to be totally false", and that's not always
a strike against the model. How do we discern the difference
between a good asterisk and a bad one? How common the
circumstance; how recognizable as /being/ a special circumstance;
how disproportionate a truth discrepancy we're talking about; etc.
I know I've said this before. If I'm in a class being taught how
this stuff works, and the teacher says "Now unsafe concurrent
code can break this in horrible ways, and in $otherClass you will
learn what's really going on in the presence of data races" ... I
feel fully satisfied by that. I know I won't get away with
playing fast and loose with The Concurrency Rules; I'm not
advanced enough and might never be. (Many people aren't but
/don't /know it, and therein lies the problem, but do we really
have much power to protect such people from themselves?)
I could be wrong, but I suspect this kind of viewpoint might be
more common and respected in the wider world than it is among the
rarefied kind of individuals who join expert groups, no offense
to anyone here meant. You're always going to see all the details,
and you're always going to /want/ to see all the details. The
general public just hopes the details stay out of their way. When
they don't, they have a bad day, but it doesn't mean they were
better served by a complex model that tried to account for
everything.
--
Kevin Bourrillion | Java Librarian | Google, Inc. |[email protected]
--
Kevin Bourrillion | Java Librarian | Google, Inc. |[email protected]