> On Feb 7, 2017, at 1:44 PM, Tanner Nelson <[email protected]> wrote:
> 
> 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.
> 

This kind of enum usage is a common source of API fragility. A potential 
workaround is to use a struct. E.g.:

public struct SocketError: Error {
        let id: Int
        private init(_ id: Int) { self.id = id }

        // .. common functionality goes here
        static let broken: SocketError { return SocketError(0) }
        static let closed: SocketError { return SocketError(1) }
        ...
}

And the use site would be:

do {…}
catch let error as SocketError {
        if error == .broken {
                … 
        } else {
                … default handling ...  
        }
}

(this is very similar to how the NS_[EXTENSIBLE]_ENUMs are imported into Swift; 
you get layout-equivalent with C for free)

Another alternative is to have “SocketError” be a protocol, and “BrokenPipe” be 
an uninhabitable type that conforms to it (i.e. case-less enum). Such errors 
could then also be phantom types.



> Tanner Nelson
> Vapor 
> +1 (435) 773-2831
> 
>> On Feb 7, 2017, at 10:32 PM, Michael Ilseman <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> 
>>> On Feb 7, 2017, at 1:21 PM, Tanner Nelson <[email protected] 
>>> <mailto:[email protected]>> 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 <[email protected] 
>>> <mailto:[email protected]>> 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 
>>>>> <[email protected] <mailto:[email protected]>> 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
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>> 
>> 
> 

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

Reply via email to