<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
I've been bothered by an uncomfortable feeling that .val and ! are
somehow different in nature, but haven't been able to put my finger
on it. Let me make another attempt. <br>
<br>
The "bang" and "question" operators operate on types. In the
strictest form, the bang operator takes a type that has null in its
value set, and returns a type whose value set is the same, except
for null. But observe that if the value set contains null, then
the type has to be a reference type. And the resulting type also
has to be a reference type (except maybe for weird classes like
Void) because we're preserving the remaining values, which are
references. So we could say:<br>
<br>
bang :: RefType -> RefType<br>
<br>
Bang doesn't change the ref-ness, or id-ness, of a type, it just
excludes a specific value from the value set. <br>
<br>
Now, what do ref and val do? They don't operate on types, they
operates on _classes_, to produce a type. Val can only be applied
to value classes, and produces a value type. In the strictest
interpretation (for consistency with bang), ref also only operates
on value classes. So:<br>
<br>
val :: ValClass -> ValType<br>
ref :: ValClass -> RefType<br>
<br>
Now, we've been strict with bang and ref to say they only work when
they have a nontrivial effect, and could totalize them in the
obvious way (ref is a no-op on an id class; bang is a no-op on a
value type.) Which would give us:<br>
<br>
bang :: Type -> Type<br>
val :: ValClass -> ValType<br>
ref :: Class -> RefType<br>
<br>
with the added invariant that bang preserves
id-ness/val-ness/ref-ness of types. <br>
<br>
But still, bang and ref operate on different things, and and produce
different things; one takes a type and yields a slightly refined
type with similar characteristics, the other takes a class and
yields a type with highly specific characteristics. We can conclude
a lot from `val` (its a value type, which already says a lot), but
we cannot conclude anything other than non-nullity from `bang`; it
might be a ref or a val type, it might come from an identity or
value class. <br>
<br>
What this says to me is "val is a subtype of bang"; all vals are
bangs, but not all bangs are vals. <br>
<br>
A harder problem is what to do about `question`. The strict
interpretation says we can only apply `question` to a type that is
already non-null. In our world, that's ValType. <br>
<br>
question :: ValType -> Type<br>
<br>
Or we could totalize as we did with bang, and we get an invariant
that question preserves id-ness, val-ness, ref-ness. But, what does
`question` really mean? Null is a reference.
So there are two interpretations: that question always yields a
reference type (which means non-references need to be lifted/boxed),
or that question yields a union type. <br>
<br>
It turns out that the latter is super-useful on the stack but kind
of sucks in the heap. The return value of `Map::get`, which we've
been calling `T.ref`, really wants a union type (T or Null);
similarly, many difficult questions in pattern matching might be
made less difficult with a `T or Null` Type. But there is no
efficient heap-based representation for such a union type; we could
use tagged unions (blech) or just fall back to boxing. Which leaves
us with the asymmetry that bang is representation-preserving (as
well as other things), but question is not. (Which makes sense in
that one is subtractive and the other is additive.) <br>
<br>
So, to your question: is this permanently gross? I think if we
adopt the strictest intepretations:<br>
<br>
- bang is only allowed on types that are already nullable<br>
- question is only allowed on types that are not nullable (or on
type variables)<br>
- val is only allowed on value classes<br>
- ref is only allowed on value classes (or on type variables)<br>
<br>
(And we can possibly boil away the last one, since if we can say
`T?`, there is no need for `T.ref` anywhere.) <br>
<br>
What this means is that you can say `String!`, but not `Optional!`,
because Optional is already null-free. Which means there is never
any question whether you say `X.val` or `X!` or `X.val!` (or
`X.ref!` if we exclude ref entirely). So then, rather than two ways
to say the same thing, there are two ways to say two different
things, which have different absolute strengths. <br>
<br>
This is somewhat unfortunate, but not "permanently gross." <br>
<br>
If we drop `ref` in favor of `?` (not necessarily a slam-dunk), we
can consider finding another way to spell `.val` which is less
intrusive, though there are not too many options that don't look
like line noise. <br>
<br>
<br>
<br>
<br>
<br>
<div class="moz-cite-prefix">On 6/15/2022 12:41 PM, Kevin
Bourrillion wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAGKkBkttZ35rvuZ7Exfe6Ozh1CJS0NSmTFKMrQ-K+sW1N_66Vg@mail.gmail.com">
<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>
</blockquote>
<br>
</body>
</html>