The mega-thread about SE-0192 is a bit large, and I’d like to talk about one 
specific point.  In the review conversation, there has been significant 
objection to the idea of requiring a ‘default’ for switches over enums that are 
non-exhaustive.  

This whole discussion is happening because the ABI stability work is 
introducing a new concept to enums - invisible members/inhabitants (and making 
them reasonably prominent).  A closely related feature is that may come up in 
the future is "private cases”.  Private cases are orthogonal to API evolution 
and may even occur on one that is defined to be exhaustive.

Private cases and non-exhaustive enums affect the enum in the same way: they 
say that the enum can have values that a client does not know about.  Swift 
requires switches to process *all* of the dynamically possible values, which is 
why the original proposal started out with the simplest possible solution: just 
require ‘default' when processing the cases.


The problems with “unknown case:”

The popular solution to this probably is currently being pitched as a change to 
the proposal (https://github.com/apple/swift-evolution/pull/777) which 
introduces a new concept “unknown case” as a near-alias for ‘default’:
https://github.com/jrose-apple/swift-evolution/blob/60d8698d7cde2e1824789b952558bade541415f1/proposals/0192-non-exhaustive-enums.md#unknown-case

In short, I think this is the wrong way to solve the problem.  I have several 
concerns with this:

1) Unlike in C, switch is Swift is a general pattern matching facility - not a 
way of processing integers.  It supports recursive patterns and values, and 
enums are not necessarily at the top-level of the pattern.   
https://github.com/apple/swift/blob/master/docs/PatternMatching.rst is a 
document from early evolution of Swift but contains a good general introduction 
to this.

2) Swift also has other facilities for pattern matching, including ‘if case’.  
Making switch inconsistent with them is not great.

3) As pitched, “unknown case” will match *known* cases too, which is (in my 
opinion :-) oxymoronic.

4) “unknown case:” changes the basic swift grammar (it isn’t just a modifier on 
case) because case *requires* a pattern.  A better spelling would be “unknown 
default:” which is closer to the semantic provided anyway.

5) It is entirely reasonable (though rare in practice) to want to handle 
default and unknown cases in the same switch.


For all the above reasons, ‘unknown case:' becomes a weird wart put on the side 
of switch/case, not something that fits in naturally with the rest of Swift.


Alternative proposal:

Instead of introducing a new modifier on case/switch, lets just introduce a new 
pattern matching operator that *matches unknown cases*, called “#unknown” or 
“.#unknown” or something (I’m not wed to the syntax, better suggestions welcome 
:).

In the simple case most people are talking about, instead of writing “unknown 
case:” you’d write “case #unknown:” which isn’t much different.  The nice thing 
about this is that #unknown slots directly into our pattern matching system.  
Here is a weird example:

switch someIntEnumTuple {
case (1, .X):   … matches one combination of int and tuple...
case (2, .Y):   … matches another combination of int and tuple...
case (_, #unknown): …  matches any int and any unknown enum case ...
case default:  … matches anything ...
}

Furthermore, if you have a switch that enumerates all of the known cases and 
use #unknown, then it falls out of the model that new cases (e.g. due to an SDK 
upgrade or an updated source package) produces the existing build error.  As 
with the original proposal, you can always choose to use “default:” instead of 
“case #unknown:” if you don’t like that behavior.

Of course, if you have an exhaustive enum (e.g. one defined in your own module 
or explicitly marked as such) then #unknown matches nothing, so we should warn 
about it being pointless.


This addresses my concerns above:

1) This fits into patterns in recursive positions, and slots directly into the 
existing grammar for patterns.  It would be a very simple extension to the 
compiler instead of a special case added to switch/case.

2) Because it slots into the pattern grammar, it works directly with 'if case’ 
and the other pattern matching stuff.

3) Doesn’t match known cases.

4) Doesn’t change the case grammar, it just adds a new pattern terminal 
production.

5) Allows weird cases like the example above.


All that said, the #unknown spelling isn’t great, but I’m sure we can find 
something else nice.

Thoughts?

-Chris


_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to