<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Feb 7, 2017, at 1:44 PM, Tanner Nelson &lt;<a href="mailto:tanner@qutheory.io" class="">tanner@qutheory.io</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">To give a concrete example, some of the issues have arisen from using enums as Swift.Errors.<div class=""><br class=""></div><div class="">```</div><div class="">public enum SocketError: Error {</div><div class="">&nbsp; &nbsp; case closed</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">Then we find some new error that was unaccounted for and want to add a new case.</div><div class=""><br class=""></div><div class="">```</div><div class="">public enum SocketError: Error {</div><div class="">&nbsp; &nbsp; case closed</div><div class="">&nbsp; &nbsp; case brokenPipe</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">What should have been a minor or possibly even a patch bump now requires a major bump.&nbsp;</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class="">```</div><div class="">do {</div><div class="">&nbsp; &nbsp; try someSocketThing()</div><div class="">} catch let error as SocketError {</div><div class="">&nbsp; &nbsp; switch error {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; case .closed:</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("the socket closed")</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; default:</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("there was an error with the socket")</div><div class="">&nbsp; &nbsp; }</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">In this situation, a sensible default is easy and expected.&nbsp;</div><div class=""><br class=""></div><div class="">On the other hand, something like the following obviously should be closed and allow exhaustive switching.</div><div class=""><br class=""></div><div class="">```</div><div class="">public enum AutonomousCarAction {</div><div class="">&nbsp; &nbsp; case turnLeft</div><div class="">&nbsp; &nbsp; case turnRight</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div></div></div></blockquote><div><br class=""></div><div>This kind of enum usage is a common source of API fragility. A potential workaround is to use a struct. E.g.:</div><div><br class=""></div><div>public struct SocketError: Error {</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>let id: Int</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>private init(_ id: Int) { self.id = id }</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>// .. common functionality goes here</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>static let broken: SocketError { return SocketError(0) }</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>static let closed: SocketError { return SocketError(1) }</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>...</div><div>}</div><div><br class=""></div><div>And the use site would be:</div><div><br class=""></div><div>do {…}</div><div>catch let error as SocketError {</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>if error == .broken {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>…&nbsp;</div><div><span class="Apple-tab-span" style="white-space:pre">        </span>} else {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>… default handling ...<span class="Apple-tab-span" style="white-space:pre">        </span></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div>}</div><div><br class=""></div><div>(this is very similar to how the NS_[EXTENSIBLE]_ENUMs are imported into Swift; you get layout-equivalent with C for free)</div><div><br class=""></div><div>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.</div><div><div class=""><br class=""></div><div class=""><br class=""></div></div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class="">
<div style="letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="font-variant-caps: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><font color="#5f5f5f" class="">Tanner Nelson</font></div><div style="" class=""><font color="#9dacd1" class="">Va</font><font color="#aeb2cf" class="">p</font><font color="#c8bacd" class="">o</font><font color="#d0becc" class="">r</font><font color="#9dacd1" class="">&nbsp;</font></div><div class=""><font color="#676767" class="">+1 (435) 773-2831</font></div></div></div></div></div></div></div>
</div>
<br class=""><div class=""><blockquote type="cite" class=""><div class="">On Feb 7, 2017, at 10:32 PM, Michael Ilseman &lt;<a href="mailto:milseman@apple.com" class="">milseman@apple.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Feb 7, 2017, at 1:21 PM, Tanner Nelson &lt;<a href="mailto:tanner@qutheory.io" class="">tanner@qutheory.io</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="content-type" content="text/html; charset=utf-8" class=""><div dir="auto" class=""><div class="">That's awesome. It looks like `<span style="background-color: rgba(255, 255, 255, 0);" class="">(planned) Open and closed enums` is exactly what I'm looking for.&nbsp;</span></div><div class=""><br class=""></div><div class="">Would it help if I created a concrete proposal for that or is it something the Swift team already has brewing?</div><div class=""><br class=""></div></div></div></blockquote><div class=""><br class=""></div><div class="">Note that open enums also have to solve ABI stability across versions.</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">An intermediary approach could be to come up with an attribute to convey your intent ala&nbsp;<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0047-nonvoid-warn.md" class="">https://github.com/apple/swift-evolution/blob/master/proposals/0047-nonvoid-warn.md</a>. The idea is to keep default behavior the same, but have something like (straw man syntax) @defaultCaseRequired.</div><div class=""><br class=""></div><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class="">Sent from my iPhone</div><div class=""><br class="">On Feb 7, 2017, at 22:01, Michael Ilseman &lt;<a href="mailto:milseman@apple.com" class="">milseman@apple.com</a>&gt; wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div class="">BTW, this will likely be part of the eventual design of “open”/resilient enums ala&nbsp;<a href="https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums" class="">https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums</a>. 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.</div><div class=""><br class=""></div><div class=""><br class=""></div><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Feb 7, 2017, at 7:12 AM, Tanner Nelson via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Hello Swift Evolution,<div class=""><br class=""></div><div class="">I'd like to propose that a warning be emitted when default cases are omitted for enums from other modules.&nbsp;</div><div class=""><br class=""></div><div class="">What this would look like:</div><div class=""><br class=""></div><div class="">OtherModule:</div><div class="">```</div><div class="">public enum SomeEnum {</div><div class="">&nbsp; &nbsp; case one</div><div class="">&nbsp; &nbsp; case two</div><div class="">}</div><div class=""><br class=""></div><div class="">public let global: SomeEnum = .one</div><div class="">```</div><div class=""><br class=""></div><div class="">executable:</div><div class="">```</div><div class="">import OtherModule</div><div class=""><br class=""></div><div class="">switch OtherModule.global {</div><div class="">&nbsp; &nbsp; case .one: break</div><div class="">&nbsp; &nbsp; case .two: break</div><div class="">&nbsp; &nbsp; ^~~~~ ⚠︎ Warning: Default case recommended for imported enums. Fix-it: Add `default: break`</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">Why:</div><div class=""><br class=""></div><div class="">Allowing the omission of a default case in an exhaustive switch makes the addition of a new case to the enum a breaking change.&nbsp;</div><div class="">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).</div><div class=""><br class=""></div><div class="">Background:</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">Related Twitter thread:&nbsp;<a href="https://twitter.com/tanner0101/status/796860273760104454" class="">https://twitter.com/tanner0101/status/796860273760104454</a></div><div class=""><br class=""></div><div class="">Looking forward to hearing your thoughts.</div><div class=""><br class=""></div><div class="">Best,</div><div class="">Tanner</div><div class=""><br class=""></div><div class=""><div style="font-family:'sf ui text';font-size:12px" class=""><font color="#5f5f5f" class="">Tanner Nelson</font></div><div style="font-family:'sf ui text';font-size:12px" class=""><font color="#9dacd1" class="">Va</font><font color="#aeb2cf" class="">p</font><font color="#c8bacd" class="">o</font><font color="#d0becc" class="">r</font><font color="#9dacd1" class="">&nbsp;</font></div><div style="font-family:'sf ui text';font-size:12px" class=""><font color="#676767" class="">+1 (435) 773-2831</font></div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div></div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></div></blockquote></div><br class=""></div></blockquote></div></div></blockquote></div><br class=""></div></div></blockquote></div><br class=""></div></div></div></blockquote></div><br class=""></body></html>