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

Matthew Johnson matthew at anandabits.com
Tue Feb 7 14:22:42 CST 2017


> On Feb 7, 2017, at 1:45 PM, Robert Widmann via swift-evolution <swift-evolution at swift.org> wrote:
> 
> I lean +1, but this answer on its own seems incomplete.  Exhaustiveness is an important property, and it’s not clear what happens here now when you fall through a “complete” case tree without matching a pattern, and in that sense this plan solves a problem.  But it also would hinder a “future-you” from going back and dealing with the ramifications of swapping a dependency for a newer version, in that with a default now covering the rest of the cases the compiler is unable to tell you which cases you are actually missing post-upgrade.
> 
> Library Evolution <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst> includes what I think is the more complete response here: An @closed attribute for enums where a switch is guaranteed to be exhaustive.  (Though, after the open discussion, I wonder if that keyword can’t be reused in some way to provide the inverse restriction instead).

I agree that we probably need to allow both and the question is whether we have a default or not, and if we do, which one is the default.  

Personally, if we have a default I would prefer for the default to be closed.  One of the most important attributes of enums is the fact that they encompass a fixed set of known cases.  I *want* my code to break if a new case is added to an enum of another module I depend on.  As the Library Evolution document notes "adding new cases should not be done lightly. Any clients attempting to do an exhaustive switch over all enum cases will likely not handle new cases well.”.  Perhaps if a module author is not willing to commit to a fixed set of cases maybe publicly exposing an enum’s cases is not the best way to model the API in question.

I understand that making closed the default is the is slightly contrary to the principle that a module author explicitly opts-in to public API contracts.  With that in mind, I would also consider it acceptable to decide that there should be *no* default, as we did in the `open` discussion.

It’s also worth noting that in the context of enums there are two ways in which an enum might be considered open - open to future extension by the module itself and open to extension by other modules.  The latter most closely matches the meaning of the `open` access modifier as it is currently used and I believe it has been requested on the list once or twice.  With that in mind, I don’t think we should reuse the `open` keyword to mean “open to extension in a future version of the module, but not open to extension by users of the module”.  That is basically equivalent to the meaning of `public` for classes.

This leads me to conclude that maybe the right answer here is to require library authors to specify `closed` *or* `public` when exposing an enum outside the module.  This follows the principle of library authors explicitly opting in to public API contracts, but also does not penalize the library author with additional verbosity for the contract which is more likely to lead to correct client code.


> 
>> On Feb 7, 2017, at 10: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
> 
> _______________________________________________
> 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/20170207/56cad214/attachment.html>


More information about the swift-evolution mailing list