[swift-evolution] Warning when omitting default case for imported enums

Tanner Nelson tanner at qutheory.io
Tue Feb 7 15:44:18 CST 2017


To give a concrete example, some of the issues have arisen from using enums as Swift.Errors.

```
public enum SocketError: Error {
    case closed
}
```

Then we find some new error that was unaccounted for and want to add a new case.

```
public enum SocketError: Error {
    case closed
    case brokenPipe
}
```

What should have been a minor or possibly even a patch bump now requires a major bump. 

Looking back, it would have been easy to use a different data structure for these types of errors. But there's something so elegant about handling error enums like:

```
do {
    try someSocketThing()
} catch let error as SocketError {
    switch error {
        case .closed:
            print("the socket closed")
        default:
            print("there was an error with the socket")
    }
}
```

In this situation, a sensible default is easy and expected. 

On the other hand, something like the following obviously should be closed and allow exhaustive switching.

```
public enum AutonomousCarAction {
    case turnLeft
    case turnRight
}
```

What I'm getting at here is I think it makes a lot of sense to allow the library developer to choose open or closed. I'd lean toward closed being the default since it seems to be the safer option--at least in terms of applications at runtime. Adding new options to a closed enum will cause the code not to compile, but at least consumers of the API aren't forced into adding "I don't know what to do here" default cases.

Tanner Nelson
Vapor 
+1 (435) 773-2831

> On Feb 7, 2017, at 10:32 PM, Michael Ilseman <milseman at apple.com> wrote:
> 
> 
>> On Feb 7, 2017, at 1:21 PM, Tanner Nelson <tanner at qutheory.io <mailto:tanner at qutheory.io>> wrote:
>> 
>> That's awesome. It looks like `(planned) Open and closed enums` is exactly what I'm looking for. 
>> 
>> Would it help if I created a concrete proposal for that or is it something the Swift team already has brewing?
>> 
> 
> Note that open enums also have to solve ABI stability across versions.
> 
> Basically, it sounds like you have an enum for which there is a reasonable default handling in client code for any cases added in future versions of your framework. Note that this is not true of all enums. The current behavior of breaking clients when new cases are added forces them to think about this new variant and their assumptions associated therein. This is very desirable for many enums, even public ones.
> 
> An intermediary approach could be to come up with an attribute to convey your intent ala https://github.com/apple/swift-evolution/blob/master/proposals/0047-nonvoid-warn.md <https://github.com/apple/swift-evolution/blob/master/proposals/0047-nonvoid-warn.md>. The idea is to keep default behavior the same, but have something like (straw man syntax) @defaultCaseRequired.
> 
> 
>> Sent from my iPhone
>> 
>> On Feb 7, 2017, at 22:01, Michael Ilseman <milseman at apple.com <mailto:milseman at apple.com>> wrote:
>> 
>>> BTW, this will likely be part of the eventual design of “open”/resilient enums ala https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums>. There, the goal is to reduce both ABI and source breakage caused by this sort of thing. It seems like for your purposes, you’re less inclined to care about ABI breakage than source breakage, though that may change in the future.
>>> 
>>> 
>>> 
>>>> On Feb 7, 2017, at 7:12 AM, Tanner Nelson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> 
>>>> Hello Swift Evolution,
>>>> 
>>>> I'd like to propose that a warning be emitted when default cases are omitted for enums from other modules. 
>>>> 
>>>> What this would look like:
>>>> 
>>>> OtherModule:
>>>> ```
>>>> public enum SomeEnum {
>>>>     case one
>>>>     case two
>>>> }
>>>> 
>>>> public let global: SomeEnum = .one
>>>> ```
>>>> 
>>>> executable:
>>>> ```
>>>> import OtherModule
>>>> 
>>>> switch OtherModule.global {
>>>>     case .one: break
>>>>     case .two: break
>>>>     ^~~~~ ⚠︎ Warning: Default case recommended for imported enums. Fix-it: Add `default: break`
>>>> }
>>>> ```
>>>> 
>>>> Why:
>>>> 
>>>> Allowing the omission of a default case in an exhaustive switch makes the addition of a new case to the enum a breaking change. 
>>>> In other words, if you're exhaustively switching on an enum from an imported library, the imported library can break your code by adding a new case to that enum (which the library authors may erroneously view as an additive/minor-bump change).
>>>> 
>>>> Background:
>>>> 
>>>> As a maintainer of a Swift framework, public enums have been a pain point in maintaining semver. They've made it difficult to implement additive features and have necessitated the avoidance of enums in our future public API plans.
>>>> 
>>>> Related Twitter thread: https://twitter.com/tanner0101/status/796860273760104454 <https://twitter.com/tanner0101/status/796860273760104454>
>>>> 
>>>> Looking forward to hearing your thoughts.
>>>> 
>>>> Best,
>>>> Tanner
>>>> 
>>>> Tanner Nelson
>>>> Vapor 
>>>> +1 (435) 773-2831
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
> 

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


More information about the swift-evolution mailing list