<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Jan 8, 2018, at 22:54, Chris Lattner via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">The mega-thread about SE-0192 is a bit large, and I’d like to talk about one specific point. &nbsp;In the review conversation, there has been significant objection to the idea of requiring a ‘default’ for switches over enums that are non-exhaustive. &nbsp;<div class=""><br class=""></div><div class=""><div class="">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). &nbsp;A closely related feature is that may come up in the future is "private cases”. &nbsp;Private cases are orthogonal to API evolution and may even occur on one that is defined to be exhaustive.</div><div class=""><br class=""></div><div class="">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. &nbsp;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.</div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">The problems with&nbsp;“unknown case:”</b></div><div class=""><br class=""></div><div class="">The popular solution to this probably is currently being pitched as a change to the proposal (<a href="https://github.com/apple/swift-evolution/pull/777" class="">https://github.com/apple/swift-evolution/pull/777</a>) which introduces a new concept “unknown case” as a near-alias for ‘default’:</div><div class=""><a href="https://github.com/jrose-apple/swift-evolution/blob/60d8698d7cde2e1824789b952558bade541415f1/proposals/0192-non-exhaustive-enums.md#unknown-case" class="">https://github.com/jrose-apple/swift-evolution/blob/60d8698d7cde2e1824789b952558bade541415f1/proposals/0192-non-exhaustive-enums.md#unknown-case</a><br class=""><div class=""><br class=""></div><div class="">In short, I think this is the wrong way to solve the problem. &nbsp;I have several concerns with this:</div><div class=""><br class=""></div><div class="">1) Unlike in C, switch is Swift is a general pattern matching facility - not a way of processing integers. &nbsp;It supports recursive patterns and values, and enums are not necessarily at the top-level of the pattern. &nbsp; <a href="https://github.com/apple/swift/blob/master/docs/PatternMatching.rst" class="">https://github.com/apple/swift/blob/master/docs/PatternMatching.rst</a> is a document from early evolution of Swift but contains a good general introduction to this.</div><div class=""><br class=""></div><div class="">2) Swift also has other facilities for pattern matching, including ‘if case’. &nbsp;Making switch inconsistent with them is not great.</div><div class=""><br class=""></div><div class="">3) As pitched, “unknown case” will match *known* cases too, which is (in my opinion :-) oxymoronic.</div><div class=""><br class=""></div><div class=""><div class="">4) “unknown case:” changes the basic swift grammar (it isn’t just a modifier on case) because case *requires* a pattern. &nbsp;A better spelling would be “unknown default:” which is closer to the semantic provided anyway.</div><div class=""><br class=""></div></div><div class="">5) It is entirely reasonable (though rare in practice) to want to handle default and unknown cases in the same switch.</div><div class=""><div class=""><br class=""></div></div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">Alternative proposal:</b></div><div class=""><br class=""></div><div class="">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 :).</div><div class=""><br class=""></div><div class="">In the simple case most people are talking about, instead of writing “unknown case:” you’d write “case #unknown:” which isn’t much different. &nbsp;The nice thing about this is that #unknown slots directly into our pattern matching system. &nbsp;Here is a weird example:</div><div class=""><br class=""></div><div class="">switch someIntEnumTuple {</div><div class="">case (1, .X): &nbsp; … matches one combination of int and tuple...</div><div class="">case (2, .Y): &nbsp; … matches another combination of int and tuple...</div><div class="">case (_, #unknown): … &nbsp;matches any int and any unknown enum case ...</div><div class="">case default: &nbsp;… matches anything ...</div><div class="">}</div><div class=""><br class=""></div><div class="">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. &nbsp;As with the original proposal, you can always choose to use “default:” instead of “case #unknown:” if you don’t like that behavior.</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">This addresses my concerns above:</div><div class=""><br class=""></div><div class="">1) This fits into patterns in recursive positions, and slots directly into the existing grammar for patterns. &nbsp;It would be a very simple extension to the compiler instead of a special case added to switch/case.</div><div class=""><br class=""></div><div class="">2) Because it slots into the pattern grammar, it works directly with 'if case’ and the other pattern matching stuff.</div><div class=""><br class=""></div><div class="">3) Doesn’t match known cases.</div><div class=""><br class=""></div><div class="">4) Doesn’t change the case grammar, it just adds a new pattern terminal production.</div><div class=""><br class=""></div><div class="">5) Allows weird cases like the example above.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">All that said, the #unknown spelling isn’t great, but I’m sure we can find something else nice.</div><div class=""><br class=""></div><div class="">Thoughts?</div></div></div></div></blockquote><br class=""></div><div>Thanks for writing this up, Chris! I addressed the idea of making this an arbitrary pattern&nbsp;<a href="https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums-2/proposals/0192-non-exhaustive-enums.md#unknown-case-patterns" class="">in the revised proposal</a>, and the idea of not matching known cases in&nbsp;<a href="https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums-2/proposals/0192-non-exhaustive-enums.md#mixing-unknown-case-and-default" class="">a "considered alternative"</a>, but in short:</div><div><br class=""></div><div>- Matching in arbitrary pattern positions is harder to implement and harder to produce good diagnostics for. (Specifically, the former comes from having to actually check the existing cases in the generated code rather than just having an "else" branch.) I'm not inherently opposed to it if we can all agree on what it means, but I don't think I have time to implement it for Swift 5, so I'm not including it in the proposal.</div><div><br class=""></div><div>- Matching known cases is a feature, not a limitation, to avoid existing code changing meaning when you recompile. I'll admit that's not the strongest motivation, though, since other things can change the meaning of existing code when you recompile already.</div><div><br class=""></div><div>- FWIW I can't actually think of a use case for using this with `if case` or anything else. I'm not against it, but I don't know why you would ever do it, just like I don't know why you would mix `case #unknown` with `default` when matching against a single value.</div><div><br class=""></div><div>That said, it sounds like people are happier with `case #unknown` than `unknown case`, and that leaves things a little more consistent if we ever do expand it to other pattern positions, so I'll change the proposal revision to use that spelling. (And if anyone comes up with a nicer spelling, great!)</div><div><br class=""></div><div>Jordan</div><br class=""></body></html>