On Thu, May 12, 2022 at 5:22 AM Brian Goetz <brian.go...@oracle.com> wrote:
> - there is a nullability-injecting conversion from T! to T? (this is a > widening conversion) > I think we'd expect full subtyping here, right? It needs to work for covariant arrays, covariant returns, type argument bounds, etc. > and then we get to decide: which conversions are allowed in assignment > context? Clearly a nullability-injecting conversion is OK here (assigning > String! to String? is clearly OK, it's a widening), so the question is: how > do you go from `T?` to `T!` ? Options include: > > - it's like unboxing, let the assignment through, perhaps with a warning, > and NPE if it fails > - require a narrowing cast > Yes, I do think we want a cast there (a special operator for it is very helpful so you don't have to repeat the base type), but as far as I know the case could be made either way for error vs. warning if the cast isn't there. Enter Valhalla: > > * (Let's say we have B1, B2a/B3a (atomic), and B2b/B3b ("b"reakable?)) > * On a B3 value type like `int`, `?` would be nonsense and `!` redundant. > * That's equally true of a B3 value type spelled `Complex.val` (if such a > thing exists). > * (assuming `Complex` is ref-default) all three of `Complex`, `Complex?`, > and `Complex!` have valid and distinct meanings. > > > If we have both .val and nullity annotations I think we are losing. The > idea here would be that B3.val *is literally spelled* `B3!`. The > declaration story is unchanged: class B1 / value-based class B2 / [ > non-atomic ] value class B3, for some suitable spellings. > I have tried to write the above to account for *that* possibility and for the subtly different possibility that you don't "spell .val" at all, you just express your nullability needs and the system optimizes to a value type when it can. Now, imagining that we reached this point... would B3a/B3b (as a > language-level thing) become immediately vestigial?. > > Unfortunately not. We need permission to unleash the zero-default type, > because many B2 types (e.g., LocalDate) have no good zero. So B3 is needed > to unlock that. > (Sorry to be a skipping record, but *no* type has a great default value. It's just about tolerable levels of badness. We tolerate `long` and will tolerate `ulong` because we're habituated to it. At best it is sometimes a tiny convenience. It's never exactly an *advantage* to be unable to distinguish whether a variable was ever initialized.) But, suppose the *class* is identifiable in some way as friendly to that default value. I'm still struggling to think through whether we also strictly need to have something at the use site equivalent to `.val`. Or if just knowing the nullness bit is enough. It may be fundamentally the same question you're asking; I'm not sure. What this short discussion has revealed is that there really are two > interpretations of non-null here: > > - In the traditional cardinality-based interpretation, T! means: "a > reference, but it definitely holds an instance of T, so you better have > initialized it properly" > - In the B3 interpretation, it means: "the zero (uninitialized, > not-run-through-the-ctor) value is a valid value, so you don't need to have > initialized it." > I'm not sure these are that different. I think that as types they are the same. It's the conjuring of default values, specifically, that differs: we can do it for B2, B3, and B3!, and we don't know how to find one for B2!. But that's not a complication, it's just precisely what we're saying B2 exists for: to stop that from happening. -- Kevin Bourrillion | Java Librarian | Google, Inc. | kev...@google.com