Here is my current concept of this beast:


The next installment of this is: how does assignment and conversion work?  Presumably, it starts with:

 - there is a null-discarding conversion from T? to T! (this is a narrowing conversion)  - there is a nullability-injecting conversion from T! to T? (this is a widening conversion)

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

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.

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.

With Complex as a B2a or B2b, would `Complex!` ever not optimize to the B3-like *implementation*? I think the (standard) primitives could be understood as B2 themselves, with `int` just being an alias for `Integer!`.

A B3:

    value class Integer { ... } // int is alias for Integer!

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."

    Con: To the extent full emotional types do not align clearly with
    primitive type projections, we might be painted into a corner and
    it might be harder to do emotional types.


I'm questioning whether we would need primitive type projections at all, just nullable/non-null type projections.

Indeed, that was the point of my query.

Reply via email to