<html><head><style>body{font-family:Helvetica,Arial;font-size:13px}</style></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;">I’d very much in favour of a consistent access modifiers across the whole language and eliminate exclusive `open`. `open/public` protocols are more than welcome. Plus it’s already has been said that Swift will potentially support subtyping for value type in some future, where we’ll yet again would need to align what `public` and `open` will mean. So I’d appreciate all the steps that could already be made now to align their meaning as much as it’s possible to this moment.</div> <br> <div id="bloop_sign_1502308443675845120" class="bloop_sign"></div> <br><p class="airmail_on">Am 9. August 2017 um 18:08:09, Matthew Johnson via swift-evolution (<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>) schrieb:</p> <blockquote type="cite" class="clean_bq"><span><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div></div><div>
<title></title>
Hi Jordan,
<div class=""><br class=""></div>
<div class="">Thanks for bringing this topic up again! I’m
glad to see it will receive attention in Swift 5. I agree
with the semantics of your proposed direction. </div>
<div class=""><br class=""></div>
<div class="">In terms of syntax, I continue to believe that
requiring users to specify a keyword indicating open or closed *in
addition* to public would be unfortunate. Open / closed is
only relevant for public enums and therefore implies public.
We’ve done a really good job of avoiding keyword soup in
Swift and the way that open classes are implicitly public is a good
precedent that we should follow.</div>
<div class=""><br class=""></div>
<div class="">I also continue to believe that aligning protocols,
enums and classes to use consistent terminology for similar
concepts has many advantages. The semantics would be:</div>
<div class=""><br class=""></div>
<div class="">* open: Extensible outside the library</div>
<div class="">* public: Extensible in future versions of the
library (or privately by the library)</div>
<div class="">* closed: Fixed set of publicly visible cases /
subclasses / conformances defined by the library and guaranteed not
to change without breaking ABI and source compatibility.</div>
<div class=""><br class=""></div>
<div class="">This approach makes public a “soft default” that
preserves maximum flexibility for the library author while allowing
them to make a stronger guarantee of user-extensibility or
completeness by changing (rather than adding) a keyword. It
also highlights the symmetry of the two very different
user-guarantees a library may choose to support.</div>
<div class=""><br class=""></div>
<div class="">As noted in my previous thread, this approach would
require a migration for protocols as well as enums as the current
behavior of public protocols is to allow conformances outside the
library.</div>
<div class=""><br class=""></div>
<div class="">There are certainly reasonable arguments to be made
for other approaches, particularly if there is no appetite for
changing the semantics of public protocols (which seems likely).
Nevertheless, I think we should keep the merits of
consistency in mind and understand the benefits of alternatives
relative to the more consistent approach as we evaluate them.</div>
<div class=""><br class=""></div>
<div class="">In terms of alternatives, what is your opinion on
using public as a “soft default” and assigning it one of the two
enum semantics you discuss? Do you think this makes sense or
would you prefer distinct keywords for these two semantics? I
don’t have any really great new ideas, but I’ll throw out
“complete” and “incomplete” as a possibility.</div>
<div class=""><br class=""></div>
<div class="">My two cents for now…</div>
<div class=""><br class=""></div>
<div class="">Matthew</div>
<div class=""><br class=""></div>
<div class=""><br class="">
<div>
<blockquote type="cite" class="">
<div class="">On Aug 8, 2017, at 5:27 PM, Jordan Rose via
swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div>
<br class="Apple-interchange-newline">
<div class="">
<div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">
<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 <i class="">Swift</i> 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 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 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 <i class="">terribly</i> 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 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 still be private cases
defined in a .m file?<br class="">
- If there's an Objective-C enum in <i class="">another</i> framework (that I built locally with Xcode,
Carthage, CocoaPods, SwiftPM, etc.), should it allow exhaustive
switching, because there are no <i class="">binary</i> compatibility issues, or not, because there may
be <i class="">source</i> compatibility issues? We'd
really like adding a new enum case to <i class="">not</i> be a 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 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 may be source
compatibility issues? Again, we'd really like adding a new enum
case to <i class="">not</i> 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 you must have a 'default' in a switch". In
previous (in-person) discussions 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 also 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 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 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> 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 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 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> 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>
</div>
_______________________________________________<br class="">
swift-evolution mailing list<br class="">
<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">
https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div>
</blockquote>
</div>
<br class=""></div>
_______________________________________________<br>swift-evolution mailing list<br>swift-evolution@swift.org<br>https://lists.swift.org/mailman/listinfo/swift-evolution<br></div></div></span></blockquote></body></html>