[swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

Matthew Johnson matthew at anandabits.com
Wed Jan 3 12:06:56 CST 2018


> On Jan 3, 2018, at 11:49 AM, Dave DeLong <swift at davedelong.com> wrote:
> 
> 
> 
>> On Jan 3, 2018, at 10:36 AM, Matthew Johnson <matthew at anandabits.com <mailto:matthew at anandabits.com>> wrote:
>> 
>> 
>>> On Jan 3, 2018, at 11:07 AM, Dave DeLong via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> IMO this is still too large of a hammer for this problem.
>>> 
>>> This whole “unexpected case” thing is only a problem when you’re linking libraries that are external to/shipped independently of your app. Right now, the *only* case where this might exist is Swift on the server. We *might* run in to this in the future once the ABI stabilizes and we have the Swift libraries shipping as part of iOS/macOS/Linux. Other than this, unexpected enum cases won’t really be a problem developers have to deal with.
>>> 
>>> Because this will be such a relatively rare problem, I feel like a syntax change like what’s being proposed is a too-massive hammer for such a small nail.
>>> 
>>> What feels far more appropriate is:
>>> 
>>> 🅰️ Teaching the compiler/checker/whatever about the linking semantics of modules. For modules that are packaged inside the final built product, there is no need to deal with any unexpected cases, because we already have the exhaustiveness check appropriate for that scenario (regardless of whether the module is shipped as a binary or compiled from source). The app author decides when to update their dependencies, and updating those dependencies will produce new warnings/errors as the compiler notices new or deprecated cases. This is the current state of things and is completely orthogonal to the entire discussion.
>> 
>> John McCall sketched out a vision of what a solution to this might look like here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171218/042333.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171218/042333.html>. 
>> 
>>> 
>>> and
>>> 
>>> 🅱️ Adding an attribute (@frozen, @tangled, @moana, @whatever) that can be used to decorate an enum declaration. This attribute would only need to be consulted on enums where the compiler can determine that the module will *not* be part of the final built product. (Ie, it’s an “external” module, in my nomenclature). This, then, is a module that can update independently of the final app, and therefore there are two possible cases:
>>> 
>>> 	1️⃣ If the enum is decorated with @frozen, then I, as an app author, have the assurance that the enum case will not change in future releases of the library, and I can safely switch on all known cases and not have to provide a default case. 
>>> 
>>> 	2️⃣ If the enum is NOT decorated with @frozen, then I, as an app author, have to account for the possibility that the module may update from underneath my app, and I have to handle an unknown case. This is simple: the compiler should require me to add a “default:” case to my switch statement. This warning is produced IFF: the enum is coming from an external module, and the enum is not decorated with @frozen.
>> 
>> This does not help people who need to write a switch statement over an enum vended by a module that ships with the OS keep their code up to date as the module adds new cases. I find the example of `SKPaymentTransactionState` provided by Brent Royal-Gordon here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170904/039512.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170904/039512.html> to be compelling.  There are rare but legitimate reasons to switch over all known cases of a non- at frozen enum that ship with the OS.  These use cases deserve proper language support.  I think Jordan’s solution strikes a good balance.
> 
> I disagree that more is needed. In the case of the transaction state, it should not be marked as @moana, and so the compiler would force you to add a “default” case to your switch statements. The switch statements would still be exhaustive with all known cases (if you choose to handle all known cases), but you’d still need a default case because there might be new transaction states in the future.
> 
> In those cases, your app could decide what to do, if that’s possible at all. Maybe there’s other transaction information you could introspect to determine if it succeeded or is still pending or whatever, and then your app could respond as you see fit.

SKPaymentTransactionState is an excellent motivating example for a better solution because it is crucial to update code to handle any new states accurately and promptly.  This is much easier to do with compiler assistance than without it. 

It is also an excellent motivating example because the API change is driven by a business change and for which all states should be reflected accurately in the UX.  Apps need to switch over this enum as exhaustively as possible while the StoreKit team also needs to reserve the right to add cases as business requirements change.  

It is an evolution problem without a great answer.  I think Jordan has landed on a pretty good compromise.  If you don’t want a warning when modules change you’re still free to use `default`.  Nobody is talking about forcing you to use `unknown case`.  If you really dislike it you could even ban it with a linter.

> 
> Dave

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180103/42c4c39c/attachment.html>


More information about the swift-evolution mailing list