Nullable switch
Guy Steele
guy.steele at oracle.com
Fri Aug 7 04:05:50 UTC 2020
Okay, so it would seem that we need two keywords (or other syntax) for use in patterns; I will temporarily call them “anything-but-null” and “anything-including-null”.
And it seems that what Brian and Remi are arguing over is whether “anything-including-null” should be spelled “var” or “any”.
And the arguments include some good points about consistency with other parts of the language, consistent rules for refactoring, user expectations, and potential pitfalls for users.
IIUC, Remi wants to spell “anything-but-null” as “var” (at least in patterns) and “anything-including-null” as “any”.
Brian wants to spell “anything-including-null” as “var”, and I am not sure how he wants to spell “anything-but-null”.
In particular, I am uncertain about this situation: suppose we have a pattern FrogBox(Frog x) (that is, it is known that the argument to FrogBox must be of type Frog), and Tadpole is a class that extends (or implements) Frog; then consider
FrogBox fb = …;
switch (fb) {
case FrogBox(Tadpole x) -> … ;
case FrogBox(Frog x) -> … ;
}
(I think this is similar to previous examples, but I want the catchall case to be Frog, not Object.) Is the preceding switch total, or do I need to say either
FrogBox fb = …;
switch (fb) {
case FrogBox(Tadpole x) -> … ;
case FrogBox(Frog x) -> … ;
case FrogBox(var x) -> … ;
}
or
FrogBox fb = …;
switch (fb) {
case null -> … ;
case FrogBox(Tadpole x) -> … ;
case FrogBox(Frog x) -> … ;
}
in order to be total?
> On Aug 6, 2020, at 9:28 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>
>
>> If we were paying the full cost of nullable types (T?), then there would be an obvious choice: T would be a non-nullable type pattern, and T? would be a nullable type pattern. But introducing an `any Foo` notion has similar conceptual surface area, but dramatically less utility. So the "return on syntax" for "any Foo" is not good.
>>
>> It has the same utility as introducing case null.
>
> Even if I agreed with that, that's benefit, but "return" is benefit-relative-to-cost. So let's talk about cost: `case null` is not an entirely new syntactic form whose meaning is non-obvious; it is an obvious extension of `case 0` or `case "foo"`. So the incremental "cost" is effectively zero by comparison.
>
> In any case, `any Foo` is bad for multiple reasons. As is `Foo|Null`. A serious problem with both is that they are too easily forgotten, and people will think `Box(var x)` is total when it is not. In expression switches, the need for totality may save them when they get a compile error (maybe), but in statement switches, it never will.
>
>>
>> [...]
>>
>>
>>
>> The argument for using totality to wriggle out of the nullity trap is a sophisticated one, which may be part of the problem, but it is largely about uniformity (and partially a pick-your-poison.)
>>
>> I think this is a forced move: that
>>
>> case Box(var o):
>>
>> be total (including Box(null)). Any other conclusion is hard to take seriously. If this excludes Box(null), users will make frequent errors -- because users routinely ignore null's special behaviors (and we mostly want them to keep doing so.)
>>
>> You made this arguments several times but i don't understand it, why case Box(var o) has to allow null if there is a case Box(any o) which allow null exists.
>
> That's not an argument, it's just assuming your conclusion :)
>
> So, if you want to make your case, I suggest you start over, and start with problems and goals and principles rather than a (partial) solution. And I think you're going to want to work through the whole thing, and write it all up, rather than describing only an incremental diversion -- because the whole thing has to stand together, and it's way too easy to think you've solved the whole thing when you've really just moved the problem elsewhere (I've been there a few times already on this very topic.)
>
>
More information about the amber-spec-observers
mailing list