<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><br><div><br>On 9 Aug 2017, at 09:21, Adrian Zubarev via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br><br></div><blockquote type="cite"><div>Hi Jordan, is that only me or haven't you metioned the default should be applied to all new enums? Personally I'd say that 'closed' should be the default and the 'open' enum would require an extra keyword.</div></blockquote><div><br></div><div>I think it should definitely be the other way round for public enums because closed is the stronger guarantee. Final is the default for classes because open is the stronger guarantee. That’s probably why we should not use the same keywords.</div><br><blockquote type="cite"><div><div>Now about the keyword itself. Here are two keywords that IMHO nail their behavior down to the point:</div><div><br></div><div>finite enum A {} - so to say a closed enum (default)</div><div>infinite enum B {} - so to say an open enum (requires default case in a switch statement)</div><div><br></div><div>If you think the default should be the other way around, than feel free to switch that. 'finite' also implies that the enum connot ever be extended with more cases (to become infinite), which was also mentioned in your email.<br><div><div> <br><div class="bloop_sign"><div style="font-family:helvetica,arial;font-size:13px">--&nbsp;<br>Adrian Zubarev<br>Sent with Airmail</div></div> <p class="gmail_quote" style="color:#000;">Am 9. August 2017 um 00:27:53, Jordan Rose via swift-evolution (<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>) schrieb:</p> <blockquote type="cite" class="gmail_quote"><span><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div></div><div>



<title></title>


<div dir="auto" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi, everyone. Now that Swift 5 is starting up, I'd like to
circle back to an issue that's been around for a while: the source
compatibility of enums. Today, it's an error to switch over an enum
without handling all the cases, but this breaks down in a number of
ways:
<div class=""><br class=""></div>
<div class="">- A C enum may have "private cases" that aren't
defined inside the original enum declaration, and there's no way to
detect these in a switch without dropping down to the
rawValue.</div>
<div class="">- For the same reason, the compiler-synthesized
'init(rawValue:)' on an imported enum never produces 'nil', because
who knows how anyone's using C enums anyway?</div>
<div class="">- Adding a new case to a&nbsp;<i class="">Swift</i>&nbsp;enum in a library breaks any client code that was
trying to switch over it.</div>
<div class=""><br class=""></div>
<div class="">(This list might sound familiar, and that's because
it's from a message of mine on a thread started by Matthew Johnson
back in February called "[Pitch] consistent public access
modifiers". Most of the rest of this email is going to go the same
way, because we still need to make progress here.)</div>
<div class=""><br class=""></div>
<div class="">At the same time, we really like our exhaustive
switches, especially over enums we define ourselves. And there's a
performance side to this whole thing too; if&nbsp;all cases of an
enum are known, it can be passed around much more efficiently than
if it might suddenly grow a new case containing a struct with
5000&nbsp;Strings in it.</div>
<div class=""><br class=""></div>
<div class=""><br class=""></div>
<div class=""><b class="">Behavior</b><br class="">
<br class="">
I think there's certain behavior that is probably
not&nbsp;<i class="">terribly</i>&nbsp;controversial:<br class="">
<br class="">
- When enums are imported from Apple frameworks, they should always
require a default case, except for a few exceptions like
NSRectEdge. (It's Apple's job to handle&nbsp;this and get it right,
but if we get it wrong with an imported enum there's still the
workaround of dropping down to the raw value.)<br class="">
- When I define Swift enums in the current framework, there's
obviously no compatibility issues; we should allow exhaustive
switches.<br class="">
<br class="">
Everything else falls somewhere in the middle, both for enums
defined in Objective-C:<br class="">
<br class="">
- If I define an Objective-C enum in the current framework, should
it allow exhaustive switching, because there are no compatibility
issues, or not, because there could&nbsp;still be private cases
defined in a .m file?<br class="">
- If there's an Objective-C enum in&nbsp;<i class="">another</i>&nbsp;framework (that I built locally with Xcode,
Carthage, CocoaPods, SwiftPM, etc.), should it allow exhaustive
switching, because&nbsp;there are no&nbsp;<i class="">binary</i>&nbsp;compatibility issues, or not, because there may
be&nbsp;<i class="">source</i>&nbsp;compatibility issues? We'd
really like adding a new enum case to&nbsp;<i class="">not</i>&nbsp;be a&nbsp;breaking change even at the source
level.<br class="">
- If there's an Objective-C enum coming in through a bridging
header, should it allow exhaustive switching, because I might have
defined it myself, or not, because it&nbsp;might be non-modular
content I've used the bridging header to import?<br class="">
<br class="">
And in Swift:<br class="">
<br class="">
- If there's a Swift enum in another framework I built locally,
should it allow exhaustive switching, because there are no binary
compatibility issues, or not, because there&nbsp;may be source
compatibility issues? Again, we'd really like adding a new enum
case to&nbsp;<i class="">not</i>&nbsp;be a breaking change even at
the source level.<br class="">
<br class=""></div>
<div class="">Let's now flip this to the other side of the
equation. I've been talking about us disallowing exhaustive
switching, i.e. "if the enum might grow new
cases&nbsp;you&nbsp;must&nbsp;have a 'default' in a switch". In
previous (in-person) discussions&nbsp;about this feature, it's been
pointed out that the code in an otherwise-fully-covered switch is,
by definition, unreachable, and therefore untestable.
This&nbsp;also&nbsp;isn't a desirable situation to be in, but it's
mitigated somewhat by the fact that there probably aren't many
framework enums you should exhaustively switch over&nbsp;anyway.
(Think about Apple's frameworks again.) I don't have a great
answer, though.<br class="">
<br class="">
For people who like exhaustive switches, we thought about adding a
new kind of 'default'—let's call it 'unknownCase' just to be able
to talk about it. This lets&nbsp;you get warnings when you update
to a new SDK, but is even more likely to be untested code. We
didn't think this was worth the complexity.<br class="">
<br class=""></div>
<div class=""><br class=""></div>
<div class=""><b class="">Terminology</b></div>
<div class=""><b class=""><br class=""></b></div>
<div class="">The "<a href="http://jrose-apple.github.io/swift-library-evolution/" class="">Library Evolution</a>" doc (mostly written by me) originally
called these "open" and "closed" enums ("requires a default" and
"allows exhaustive switching", respectively), but this predated the
use of 'open' to describe classes and class members. Matthew's
original thread did suggest using 'open' for enums as well, but I
argued against that, for a few reasons:</div>
<div class=""><br class=""></div>
<div class="">- For classes, "open" and "non-open" restrict what
the <i class="">client</i> can do. For enums, it's more about
providing the client with additional guarantees—and "non-open" is
the one with more guarantees.</div>
<div class="">- The "safe" default is backwards: a merely-public
class can be made 'open', while an 'open' class cannot be made
non-open. Conversely, an "open" enum can be made "closed" (making
default cases unnecessary), but a "closed" enum cannot be made
"open".</div>
<div class=""><br class=""></div>
<div class="">That said, Clang now has an 'enum_extensibility'
attribute that does take 'open' or 'closed' as an argument.</div>
<div class=""><br class=""></div>
<div class="">On Matthew's thread, a few other possible names came
up, though mostly only for the "closed" case:</div>
<div class=""><br class=""></div>
<div class="">- 'final': has the right meaning abstractly, but
again it behaves differently than 'final' on a class, which is a
restriction on code elsewhere in the same module.</div>
<div class="">- 'locked': reasonable, but not a standard term, and
could get confused with the concurrency concept</div>
<div class="">- 'exhaustive': matches how we've been explaining it
(with an "exhaustive switch"), but it's not exactly the <i class="">enum</i>&nbsp;that's exhaustive, and it's a long keyword to
actually write in source.</div>
<div class=""><br class=""></div>
<div class="">- 'extensible': matches the Clang attribute, but also
long</div>
<div class=""><br class=""></div>
<div class=""><br class=""></div>
<div class="">I don't have better names than "open" and "closed",
so I'll continue using them below even though I avoided them above.
But I would <i class="">really like to find some</i>.</div>
<div class=""><br class=""></div>
<div class=""><br class=""></div>
<div class=""><b class="">Proposal</b></div>
<div class=""><b class=""><br class=""></b></div>
<div class="">Just to have something to work off of, I propose the
following:</div>
<div class=""><br class=""></div>
<div class="">1. All enums (NS_ENUMs) imported from Objective-C are
"open" unless they are declared "non-open" in some way (likely
using the&nbsp;enum_extensibility attribute mentioned above).</div>
<div class="">2. All public Swift enums in modules compiled "with
resilience" (still to be designed) have the option to be either
"open" or "closed". This only applies to libraries not distributed
with&nbsp;an app, where binary compatibility is a
concern.<br class="">
3. All public Swift enums in modules compiled from source have the
option to be either "open" or "closed".</div>
<div class="">4. In Swift 5 mode, a public enum should be <i class="">required</i> to declare if it is "open" or "closed", so that
it's a conscious decision on the part of the library author. (I'm
assuming we'll have a "Swift 4 compatibility mode" next year that
would leave unannotated enums as "closed".)</div>
<div class="">5. None of this affects non-public enums.</div>
<div class=""><br class=""></div>
<div class="">(4) is the controversial one, I expect. "Open" enums
are by far the common case in Apple's frameworks, but that may be
less true in Swift.</div>
<div class=""><br class=""></div>
<div class=""><br class=""></div>
<div class=""><b class="">Why now?</b></div>
<div class=""><br class=""></div>
<div class="">Source compatibility was a big issue in Swift 4, and
will continue to be an important requirement going into Swift 5.
But this also has an impact on the ABI: if an enum is "closed", it
can be accessed more efficiently by a client. We don't <i class="">have</i>&nbsp;to do this before ABI stability—we could access
all enums the slow way if the library cares about binary
compatibility, and add another attribute for this distinction
later—but it would be nice™ (an easy model for developers to
understand) if "open" vs. "closed" was also the primary distinction
between "indirect access" vs. "direct access".</div>
<div class=""><br class=""></div>
<div class="">I've written quite enough at this point. Looking
forward to feedback!</div>
<div class="">Jordan<br class=""></div>
</div>


_______________________________________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br></div></div></span></blockquote>

</div></div></div></div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></body></html>