Next up for patterns: type patterns in switch
Guy Steele
guy.steele at oracle.com
Tue Aug 11 22:37:20 UTC 2020
> On Aug 11, 2020, at 6:26 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>
>
>> On the other hand, I think Remi’s point about totality being an implicit and non-local property that is easily undermined by code changes in another compilation unit is worrisome.
>
> ... which in turn derives from something else worrisome (a problem we bought last year): that it is not clear from looking at a switch whether it is exhaustive or not. Expression switches must exhaustive, but statement switches need not be. Here, we are saying that exhaustive switch statements are a useful new thing (which they are) and get rewarded with new behaviors (some may not find it a reward), but you have to look too closely to determine whether the switch is total. If it is, the last clause is total too (well, unless it is an enum switch that names all the constants, or a switch over a sealed type that names all the sub-types but without a catch-all.)
>
> So I claim that, if there is a problem, it is that it should be more obvious that a switch is exhaustive on its target.
>
>> Putting this all together, I reach two conclusions:
>>
>> (1) We can live with the current definition of instanceof, provided we make it clear that instanceof is not purely equivalent to pattern matching, and that instanceof and pattern matching can be simply defined in terms of each other.
>
> I think we can do slightly better than this. I argue that
>
> x instanceof null
>
> is silly because we can just say
>
> x == null
>
> instead (which is more direct), and similarly
>
> x instanceof var y
> x instance of Object o
>
> are silly because we can just say
>
> var y = x
>
> instead (again more direct). So let's just ban the nullable patterns in instanceof, and no one will ever notice.
>
>> (2) We have a real disagreement about switch, but I think the fault lies in the design of switch rather than with pattern matching, and the fault is this:
>>
>> Sometimes when we write
>>
>> switch (v) {
>> case Type1 x: A
>> case Type2 y: B
>> case Type3 z: C
>> }
>>
>> we mean for Type1 and Type 2 and Type3 to be three disparate and co-equal things—in which case it seems absurd for any of them to match null; but other times we mean for Type3 to be a catchall, in which case we do want it to match null if nothing before it has.
>
> Agreed. The fundamental concern that Remi (and Stephen, over on a-dev) have raised is that we can't tell which it is, and that is disturbing. (I still think it won't matter in reality, but I understand the concern.) The same ambiguity happens with deconstruction patterns:
>
> case Type3(var x, var y, var z) t3: ...
>
> which we can think of as "enhanced" type patterns.
Sure.
> (encouraging that our mails crossed with mostly the same observation and possible fix.)
>
>> I believe some previous discussion has focused on ways to modify the _pattern_ to indicated either an expectation of totality or a specific way of handling null. But at this point I think augmenting patterns is overkill; what we need (and all we need) is a modification to the syntax of switch to indicate an expectation of totality. I have a modest suggestion:
>>
>> switch (v) {
>> case Type1 x: A
>> case Type2 y: B
>> case Type3 z: C // Type3 is not expected to be a catchall
>> }
>>
>> switch (v) {
>> case Type1 x: A
>> case Type2 y: B
>> default case Type3 z: C // Type3 is expected to be a catchall; it is a static error if Type3 is not total on v,
>> // and Type3 will match null (unlike Type1 and Type2)
>> }
>
> And we already had another reason to want something like this: expression switches are exhaustive, statement switches are not, and we'd like to be able to engage the compiler to do exhaustiveness checking for statement switches even in the absence of patterns.
>
>> Now, I will admit that this syntax is a wee bit delicate, because adding a colon might apparently change the meaning:
>>
>> switch (v) {
>> case Type1 x: A
>> case Type2 y: B
>> default: case Type3 z: C
>> }
>
> Or `final case` or `finally <pattern>` or `default-case` or ...
>
> I am iffy about `default` because of its historical association, but I will have to re-think it in light of this idea before I have an opinion.
I don;t care about the syntax very much. I thought of “default” because it sort of communicates the right idea and is already a keyword: it says that the last clause is BOTH a case (with a pattern) but also a catchall.
I have to admit that “default case” (there is really no need for a hyphen here) is a bit wordy compared to “finally”, which is very clever but could cause some cognitive dissonance in users who think too hard about “try” (really? a case clause that is always executed before you exit the switch??).
>> but I believe that in situations that matter, the compiler can and will reject this last example on other grounds (please correct me if I am mistaken
>
> yes, the compiler can catch this.
>
> The other degree of freedom on this mini-feature is whether `default` is a hint, or whether it would be an error to not say `default` on a total pattern. I think it might be seen as a burden if it were required, but Remi might think it not strong enough if its just a hint.
Yeah, I thought about that, and decided that it would be a bad idea for the compiler to complain about the absence of “default”, in part because you don't want to feel vaguely obligated to include it in simple cases involving, for example, exhaustive use of enums.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200811/df53d41e/attachment-0001.htm>
More information about the amber-spec-experts
mailing list