[swift-evolution] Handling unknown cases in enums [RE: SE-0192]

Antoine Cœur adigitalknight at gmail.com
Tue Jan 9 23:43:38 CST 2018


I'm not in favor to distinguish #unknown and #known, as it may lead to
surprises, like something that previously was unknown becomes known on a
newer iOS version for instance. And version-dependant code is clearer if
solely handled by the `@available()` syntax.

I like the new pattern matching syntax proposal, but I feel it would also
work by naming it `default` instead of `unknown`:

    case (_, default): …  matches any int and any unknown enum case ...


Le mer. 10 janv. 2018 à 08:46, Jordan Rose via swift-evolution <
swift-evolution at swift.org> a écrit :

>
>
> On Jan 8, 2018, at 22:54, Chris Lattner via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> 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?
>
>
> Thanks for writing this up, Chris! I addressed the idea of making this an
> arbitrary pattern in the revised proposal
> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums-2/proposals/0192-non-exhaustive-enums.md#unknown-case-patterns>,
> and the idea of not matching known cases in a "considered alternative"
> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums-2/proposals/0192-non-exhaustive-enums.md#mixing-unknown-case-and-default>,
> but in short:
>
> - 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.
>
> - 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.
>
> - 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.
>
> 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!)
>
> Jordan
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180110/f73b7650/attachment.html>


More information about the swift-evolution mailing list