<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><br><br>Sent from my iPad</div><div><br>On Feb 11, 2017, at 12:38 PM, Adrian Zubarev via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br><br></div><blockquote type="cite"><div><style>
body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        padding:1em;
        margin:auto;
        background:#fefefe;
}
h1, h2, h3, h4, h5, h6 {
        font-weight: bold;
}
h1 {
        color: #000000;
        font-size: 28pt;
}
h2 {
        border-bottom: 1px solid #CCCCCC;
        color: #000000;
        font-size: 24px;
}
h3 {
        font-size: 18px;
}
h4 {
        font-size: 16px;
}
h5 {
        font-size: 14px;
}
h6 {
        color: #777777;
        background-color: inherit;
        font-size: 14px;
}
hr {
        height: 0.2em;
        border: 0;
        color: #CCCCCC;
        background-color: #CCCCCC;
display: inherit;
}
p, blockquote, ul, ol, dl, li, table, pre {
        margin: 15px 0;
}
a, a:visited {
        color: #4183C4;
        background-color: inherit;
        text-decoration: none;
}
#message {
        border-radius: 6px;
        border: 1px solid #ccc;
        display:block;
        width:100%;
        height:60px;
        margin:6px 0px;
}
button, #ws {
        font-size: 12 pt;
        padding: 4px 6px;
        border-radius: 5px;
        border: 1px solid #bbb;
        background-color: #eee;
}
code, pre, #ws, #message {
        font-family: Monaco;
        font-size: 10pt;
        border-radius: 3px;
        background-color: #F8F8F8;
        color: inherit;
}
code {
        border: 1px solid #EAEAEA;
        margin: 0 2px;
        padding: 0 5px;
}
pre {
        border: 1px solid #CCCCCC;
        overflow: auto;
        padding: 4px 8px;
}
pre > code {
        border: 0;
        margin: 0;
        padding: 0;
}
#ws { background-color: #f8f8f8; }
.bloop_markdown table {
border-collapse: collapse;
font-family: Helvetica, arial, freesans, clean, sans-serif;
color: rgb(51, 51, 51);
font-size: 15px; line-height: 25px;
padding: 0; }
.bloop_markdown table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
.bloop_markdown table tr:nth-child(2n) {
background-color: #f8f8f8; }
.bloop_markdown table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr th :first-child, table tr td :first-child {
margin-top: 0; }
.bloop_markdown table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }
.bloop_markdown blockquote{
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }
code, pre, #ws, #message {
word-break: normal;
word-wrap: normal;
}
hr {
display: inherit;
}
.bloop_markdown :first-child {
-webkit-margin-before: 0;
}
code, pre, #ws, #message {
font-family: Menlo, Consolas, Liberation Mono, Courier, monospace;
}
.send { color:#77bb77; }
.server { color:#7799bb; }
.error { color:#AA0000; }</style><div class="bloop_markdown"><p>That makes actually sense to me if we think of it that we never will get any sub-typing for enums. I’m still undecided on that sub-typing topic about value types. Just out of curiosity it would be interesting to hear from the core team if this would be a future direction for Swift or not, be it in Swift 20 or whatever. :)</p></div></div></blockquote><div>The core team has indicated support for value subtyping. It's unclear when this might happen though.</div><div><br></div><div>I am working on writing up some ideas related to this right now. I hope that helps you to see how value subtyping fits into the hypothetical Swift with `closed` as an access modifier I am proposing. This will be more of a "manifesto" style document than a proposal. It will lay out the landscape a bunch of related ideas, some of which may have ABI impact (and this be relevant for consideration now) and others will be left for future consideration.</div><br><blockquote type="cite"><div><div class="bloop_markdown">
<p>Back on the original topic: If the community feels we need something like <code>closed</code>, then so be it, I don’t mind having flexibility because it makes the language more powerful. </p></div></div></blockquote><div>We definitely need it for enums. The only question is how we spell it and whether the semantics have a clear and consistent relationship with how we treat classes, protocols and the `open` access modifier. My proposal is that we do it in a way that is clear and consistent.</div><br><blockquote type="cite"><div><div class="bloop_markdown">
<p></p></div><div class="bloop_original_html"><style>body{font-family:Helvetica,Arial;font-size:13px}</style><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;"><br></div> <br> <div id="bloop_sign_1486837980597146880" class="bloop_sign"><div style="font-family:helvetica,arial;font-size:13px">-- <br>Adrian Zubarev<br>Sent with Airmail</div></div> <br><p class="airmail_on">Am 11. Februar 2017 um 19:31:10, Xiaodi Wu (<a href="mailto:xiaodi.wu@gmail.com">xiaodi.wu@gmail.com</a>) schrieb:</p> <blockquote type="cite" class="clean_bq"><span><div><div></div><div>
<title></title>
<div dir="ltr">I think Matthew's point (with which I agree) is
that, as enums are sum types, adding or removing cases is akin to
subclassing. You can extend a public enum by adding methods just
like you can extend a public class. But just as you cannot subclass
a public class, you should not be able to add or remove cases from
a public enum.
<div class="gmail_extra"><br></div>
<div class="gmail_extra"><br>
<div class="gmail_quote">On Sat, Feb 11, 2017 at 8:37 AM, Adrian
Zubarev via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div style="word-wrap:break-word">
<div class="m_-2179860613880217272bloop_markdown">
<p>I have to correct myself here and there.</p>
<blockquote>
<p>… which would be extensible if that feature might be added to
swift one day.</p>
<p>Again, I see <code>open</code> only as a contract to
<strong>allow</strong> sub-typing, conformances and overriding to
the client, where extensibility of a type a story of it’s own.</p>
</blockquote>
</div>
<div class="m_-2179860613880217272bloop_original_html">
<div id="m_-2179860613880217272bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto">
<span class=""><br></span></div>
<span class=""><br></span>
<div id="m_-2179860613880217272bloop_sign_1486823779322220032" class="m_-2179860613880217272bloop_sign">
<div style="font-family:helvetica,arial;font-size:13px">
<span class="">-- <br>
Adrian Zubarev<br>
Sent with Airmail</span></div>
</div>
<span class=""><br></span>
<div>
<div class="h5">
<p class="m_-2179860613880217272airmail_on">Am 11. Februar 2017 um
15:33:17, Adrian Zubarev (<a href="mailto:adrian.zubarev@devandartist.com" target="_blank">adrian.zubarev@devandartist.<wbr>com</a>) schrieb:</p>
<blockquote type="cite" class="m_-2179860613880217272clean_bq">
<div style="word-wrap:break-word">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>It wasn’t my intention to drive to far way off topic with
this. The major point of my last bike shedding was that I have to
disagree with you about the potential future <code>open enum</code>
vs. <code>public enum</code> and <code>closed
enum</code>.</span></p>
<p><span><code>public</code> today does not add any guarantee to
prevent the client from extending your type. For
instance:</span></p>
<pre><span><code class="m_-2179860613880217272swift">// Module A
public class A { public init() {} }
// Module B
extension A {
convenience init(foo: Int) {
print(foo)
self.init()
}
}
</code></span></pre>
<p><span>That also implies to me that <code>open</code> as an
access modifier does not prevent extensibility.</span></p>
<p><span>Speaking of opened enums, we really do not mean <code>open
enum</code> to allow extensibility where <code>closed enum</code>
would mean the opposite. <code>closed</code> or
<code>@closed</code> by all the definitions I’ve read so far is
what the current <code>public</code> means for enums. If this is
going to be fixed to <code>closed enum</code> (<code>@closed public
enum</code>) than what we’re currently speaking of is nothing else
than <code>public enum</code>, which would be extensible if that
future might be added to swift one day.</span></p>
<p><span>Again, I see <code>open</code> only as a contract to
prevent sub-typing, conformances and overriding, where
extensibility of a type a story of it’s own.</span></p>
<p><span>Quickly compared to protocols: <code>public-but-not-open
protocol</code> from module A should remain extensible in module B.
Consistently that would mean that <code>public enum</code> is the
enum when we’re talking about future extensibility of that enum
from the clients side outside your module. You simply should be
able to add new cases directly to your enum if it’s not annotated
as <code>closed</code>. <code>open enum</code> on the other hand
makes only sense when we’d speak about sub-typing on enums or value
types in general.</span></p>
</div>
<div class="m_-2179860613880217272bloop_original_html">
<div id="m_-2179860613880217272bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto">
<span><br></span></div>
<span><br></span>
<div id="m_-2179860613880217272bloop_sign_1486822474406961920" class="m_-2179860613880217272bloop_sign">
<div style="font-family:helvetica,arial;font-size:13px">
<span>-- <br>
Adrian Zubarev<br>
Sent with Airmail</span></div>
</div>
<span><br></span>
<p class="m_-2179860613880217272airmail_on"><span>Am 11. Februar
2017 um 14:08:02, Matthew Johnson (<a href="mailto:matthew@anandabits.com" target="_blank">matthew@anandabits.com</a>) schrieb:</span></p>
<blockquote type="cite" class="m_-2179860613880217272clean_bq">
<div dir="auto">
<div>
<div><span><span><br>
<br>
Sent from my iPad</span></span></div>
<div><span><br>
On Feb 11, 2017, at 4:25 AM, Adrian Zubarev via swift-evolution
<<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:<br>
<br></span></div>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>I’m probably better describing things with some
bikeshedding code, but feel free to criticize it as much as you’d
like.</span></p>
<pre><span><code class="m_-2179860613880217272swift">//===========--------- Module A ---------===========//
@closed public enum A {
case a
}
extension A {
case aa // error, because enum is closed
}
</code></span></pre></div>
</div>
</blockquote>
<div><span>This is an error because you can't add cases in an
extension. I imagine this is how cases would be added outside
the module if we allow `open enum` in the future. But whether
or not this is allowed *within* the module is a separate question
that is orthogonal to `closed` and `open`.</span></div>
<div><span><br></span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<pre><span><code class="m_-2179860613880217272swift">
public func foo(a: A) {
switch a {
case .a:
print("done")
}
}
public enum B {
case b
}
extension B {
case bb // fine, because not-closed enums are extensible
}</code></span></pre></div>
</div>
</blockquote>
<div><span>As noted above, whether this is allowed or not *within*
the module is orthogonal to `closed`. *Outside* the module it
would only be possible for enum declared `open` (if we add this
feature in the future).</span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<pre><span><code class="m_-2179860613880217272swift">
public func bar(b: B) {
switch b {
case .b:
print("b")
default: // always needed
print("some other case")
}
}
// Sub-enum relationships
// Possible even the enum A is closed, because `@closed` only
// closes the extensibility of an enum
enum SubA : A {
case aa
}
</code></span></pre></div>
</div>
</blockquote>
<div><span>Now you're talking about value subtypes. That is
orthogonal. Also, this syntax already has a meaning (the raw
value of the enum is A) so we wouldn't be able to use it the way
you are intending here. Finally, it is misleading syntax
because what you mean here is "A is a subtype of SubA" which is
exactly the opposite of what the syntax implies.</span></div>
<div><span><br></span></div>
<div><span>All values of A are valid values of SubA, but SubA has
values that are not valid values of A.</span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<pre><span><code class="m_-2179860613880217272swift">// The following enum can have a sub-enum in the clients module
open enum C {
case c
}</code></span></pre></div>
</div>
</blockquote>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<pre><span><code class="m_-2179860613880217272swift">public func cool(c: C) {
switch c {
case .c:
print("c")
default: // always needed
print("some other case")
}
}
@closed open enum D {
case d
}
public func doo(d: D) {
switch b {
case .b:
print("b")
}
}
// The enum case is always known at any point, no matter
// where the instance comes from, right?
let subA = SubA.aa
let otherSubA = SubA.a // Inherited case
let a: A = subA // error, downgrade the sub-enum to A first
let a: A = otherSubA // okay
foo(a: subA) // error, downgrade the sub-enum to A first
foo(a: otherSubA) // okay
//===========--------- Module B ---------===========//
// Totally fine
switch A.a {
case .a:
print("done")
}
extension A {
case aa // not allowed because the enum is closed
}
extension B {
case bbb
}
switch B.b {
case .b:
print("b")
default:
print("somethine else")
}
bar(b: B.bbb) // fine, because the switch statement on enums without
// `@closed` has always`default`
// Allowed because `C` is open, and open allows sub-typing, conforming
// and overriding to the client
enum SubC : C {
case cc
}
let subC = <a href="http://SubC.cc" target="_blank">SubC.cc</a>
cool(c: subC) // okay
enum SubD : D {
case dd
}
doo(d: D.dd)// error, downgrade sub-enum to D first
</code></span></pre>
<p><span>My point here is, that we should not think of (possible)
<code>open enums</code> as enums that the client is allowed to
extend. That way we’re only creating another inconsistent case for
the <code>open</code> access modifier. As far as I can tell,
<code>open</code> as for today means “the client is allowed to
subclass/override things from a different module”.</span></p>
</div>
</div>
</blockquote>
<div><span>Yes, but subclasses are analogous to enum cases. A
subtype of an enum would remove cases. I think you are
misunderstanding the relationship of enums to classes and
protocols.</span></div>
<div><span><br></span></div>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>And I already said it hundred of times that we should
extend this to make <code>open</code> a true <em>access
modifier</em> in Swift. That said the meaning of <code>open</code>
should become:</span></p>
<ul>
<li><span>The client is allowed to sub-type (currently only classes
are supported).</span></li>
<li><span>The client is allowed to conform to open
protocols</span></li>
<li><span>The client is allowed to override open type
members</span></li>
</ul>
<p><span>This also means that extensibility is still allowed to
<code>public</code> types. Public-but-not-open classes are still
extensible today, which is the correct behavior. Extending an enum
which is not closed <em>could</em> or probably should be made
possible through extensions, because <em>I</em> cannot think of
anther elegant way for the client to do so.</span></p>
</div>
</div>
</blockquote>
<div><span>This is what `open enum` would allow. It is the
proper enum analogue of open classes.</span></div>
<div><span><br></span></div>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>That will leave us the possibility to think of sub-typing
enums in the future (I sketched it out a little above).</span></p>
</div>
</div>
</blockquote>
<div><span>Value subtyping is very interesting. I have been
working on some ideas around this but I want to keep this thread
focused.</span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>If I’m not mistaken, every enum case is known at compile
time,</span></p>
</div>
</div>
</blockquote>
<div><span>This is true today but will not always be true in the
future. That is in large part what this thread is
about.</span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>which means to me that we can safely check the case before
allowing to assign or pass an instance of a sub-enum to some of its
super-enum. (Downgrading an enum case means that you will have to
write some code that either mutates your current instance or
creates a new one which matches one of the super-enum cases.)
Furthermore that allows a clear distinction of what
<code>open</code> access modifier does and how <code>@closed</code>
behaves.</span></p>
</div>
</div>
</blockquote>
<div><span>I'm not going to comment on the rest because it is
premised on a misunderstanding of what value subtyping is.
I'm going to share some ideas around value subtyping in a new
thread as soon as I have a chance to finish putting them
together.</span></div>
<span><br></span>
<blockquote type="cite">
<div>
<div class="m_-2179860613880217272bloop_markdown">
<p><span>To summarize:</span></p>
<ul>
<li><span><code>@closed enum</code> - you’re not allowed to add new
cases to the enum in your lib + (you’re allowed to create
sub-enums)</span></li>
<li><span><code>@closed public enum</code> - you and the client are
not allowed to add new cases (+ the client is not allowed to create
sub-enums)</span></li>
<li><span><code>@closed open enum</code> - you and the client are
not allowed to add new cases (+ the client might create new
sub-enums)</span></li>
<li><span><code>enum</code> - you’re allowed to add new cases
(<code>default</code> is needed in switch statements) (+ you can
create new sub-enums)</span></li>
<li><span><code>public enum</code> - you and the client are allowed
to add new cases (+ only you are allowed to create new
sub-enums)</span></li>
<li><span><code>open enum</code> - you and the client are allowed
to add new cases (everyone can create new sub-enums)</span></li>
</ul>
<p><span>This is a lot of bike shedding of mine, and the idea might
not even see any light in Swift at all, but I’d like to share my
ideas with the community. Feel free to criticize them or flesh
something out into something real. :)</span></p>
<p><span>P.S.: If we had something like this:</span></p>
<pre><span><code class="m_-2179860613880217272swift">@closed enum X {
case x, y
func foo() {
switch self {
case .x, .y:
print("swift")
}
}
enum Z : X {
case z, zz
override func foo() {
// Iff `self` is `z` or `zz` then calling super will result in an error.
// Possible solution: always tell the client to downgrade explicitly the
// case first if there is an attempt to call super (if mutating),
// or handle all cases
switch self {
case .z, .zz:
print("custom work")
default: // or all super-enum cases
super.foo()
}
}
}
</code></span></pre></div>
<div class="m_-2179860613880217272bloop_original_html">
<div id="m_-2179860613880217272bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px;color:rgba(0,0,0,1.0);margin:0px;line-height:auto">
<span><br></span></div>
<span><br></span>
<div id="m_-2179860613880217272bloop_sign_1486806388447838976" class="m_-2179860613880217272bloop_sign">
<div style="font-family:helvetica,arial;font-size:13px">
<span>-- <br>
Adrian Zubarev<br>
Sent with Airmail</span></div>
</div>
<span><br></span>
<p class="m_-2179860613880217272airmail_on"><span>Am 11. Februar
2017 um 04:49:11, Xiaodi Wu via swift-evolution (<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>) schrieb:</span></p>
<blockquote type="cite" class="m_-2179860613880217272clean_bq">
<div>
<div>
<div dir="ltr"><span><span>On Wed, Feb 8, 2017 at 5:05 PM, Matthew
Johnson via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span>
wrote:<br></span></span>
<div class="gmail_extra">
<div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div style="word-wrap:break-word">
<div>I’ve been thinking a lot about our public access modifier
story lately in the context of both protocols and enums. I
believe we should move further in the direction we took when
introducing the `open` keyword. I have identified what I
think is a promising direction and am interested in feedback from
the community. If community feedback is positive I will flesh
this out into a more complete proposal draft.</div>
<div><br></div>
<div><br></div>
<div>Background and Motivation:</div>
<div><br></div>
In Swift 3 we had an extended debate regarding whether or not to
allow inheritance of public classes by default or to require an
annotation for classes that could be subclassed outside the
module. The decision we reached was to avoid having a default
at all, and instead make `open` an access modifier. The
result is library authors are required to consider the behavior
they wish for each class. Both behaviors are equally
convenient (neither is penalized by requiring an additional
boilerplate-y annotation).
<div><br></div>
<div>A recent thread (<a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170206/031566.html" target="_blank">https://lists.swift.org/piper<wbr>mail/swift-evolution/Week-of-M<wbr>on-20170206/031566.html</a>)
discussed a similar tradeoff regarding whether public enums should
commit to a fixed set of cases by default or not. The current
behavior is that they *do* commit to a fixed set of cases and there
is no option (afaik) to modify that behavior. The Library
Evolution document (<a href="https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums" target="_blank">https://github.com/apple/swif<wbr>t/blob/master/docs/LibraryEvol<wbr>ution.rst#enums</a>)
suggests a desire to change this before locking down ABI such that
public enums *do not* make this commitment by default, and are
required to opt-in to this behavior using an `@closed`
annotation.</div>
<div><br></div>
<div>In the previous discussion I stated a strong preference that
closed enums *not* be penalized with an additional
annotation. This is because I feel pretty strongly that it is
a design smell to: 1) expose cases publicly if consumers of the API
are not expected to switch on them and 2) require users to handle
unknown future cases if they are likely to switch over the cases in
correct use of the API.</div>
<div><br></div>
<div>The conclusion I came to in that thread is that we should
adopt the same strategy as we did with classes: there should not be
a default.</div>
<div><br></div>
<div>There have also been several discussions both on the list and
via Twitter regarding whether or not we should allow closed
protocols. In a recent Twitter discussion Joe Groff suggested
that we don’t need them because we should use an enum when there is
a fixed set of conforming types. There are at least two
reasons why I still think we *should* add support for closed
protocols.</div>
<div><br></div>
<div>As noted above (and in the previous thread in more detail), if
the set of types (cases) isn’t intended to be fixed (i.e. the
library may add new types in the future) an enum is likely not a
good choice. Using a closed protocol discourages the user
from switching and prevents the user from adding conformances that
are not desired.</div>
<div><br></div>
<div>Another use case supported by closed protocols is a design
where users are not allowed to conform directly to a protocol, but
instead are required to conform to one of several protocols which
refine the closed protocol. Enums are not a substitute for
this use case. The only option is to resort to documentation
and runtime checks.</div>
<div><br></div>
<div><br></div>
<div>Proposal:</div>
<div><br></div>
<div>This proposal introduces the new access modifier `closed` as
well as clarifying the meaning of `public` and expanding the use of
`open`. This provides consistent capabilities and semantics
across enums, classes and protocols.</div>
<div><br></div>
<div>`open` is the most permissive modifier. The symbol is
visible outside the module and both users and future versions of
the library are allowed to add new cases, subclasses or
conformances. (Note: this proposal does not introduce
user-extensible `open` enums, but provides the syntax that would be
used if they are added to the language)</div>
<div><br></div>
<div>`public` makes the symbol visible without allowing the user to
add new cases, subclasses or conformances. The library
reserves the right to add new cases, subclasses or conformances in
a future version.</div>
<div><br></div>
<div>`closed` is the most restrictive modifier. The symbol is
visible publicly with the commitment that future versions of the
library are *also* prohibited from adding new cases, subclasses or
conformances. Additionally, all cases, subclasses or
conformances must be visible outside the module.</div>
<div><br></div>
<div>Note: the `closed` modifier only applies to *direct*
subclasses or conformances. A subclass of a `closed` class
need not be `closed`, in fact it may be `open` if the design of the
library requires that. A class that conforms to a `closed`
protocol also need not be `closed`. It may also be
`open`. Finally, a protocol that refines a `closed` protocol
need not be `closed`. It may also be `open`.</div>
<div><br></div>
<div>This proposal is consistent with the principle that libraries
should opt-in to all public API contracts without taking a position
on what that contract should be. It does this in a way that
offers semantically consistent choices for API contract across
classes, enums and protocols. The result is that the language
allows us to choose the best tool for the job without restricting
the designs we might consider because some kinds of types are
limited with respect to the `open`, `public` and `closed` semantics
a design might require.</div>
<div><br></div>
<div><br></div>
<div>Source compatibility:</div>
<div><br></div>
<div>This proposal affects both public enums and public
protocols. The current behavior of enums is equivalent to a
`closed` enum under this proposal and the current behavior of
protocols is equivalent to an `open` protocol under this
proposal. Both changes allow for a simple mechanical
migration, but that may not be sufficient given the source
compatibility promise made for Swift 4. We may need to
identify a multi-release strategy for adopting this proposal.</div>
<div><br></div>
<div>Brent Royal-Gordon suggested such a strategy in a discussion
regarding closed protocols on Twitter:</div>
<div><br></div>
<div>* In Swift 4: all unannotated public protocols receive a
warning, possibly with a fix-it to change the annotation to
`open`.</div>
<div>* Also in Swift 4: an annotation is introduced to opt-in to
the new `public` behavior. Brent suggested `@closed`, but as
this proposal distinguishes `public` and `closed` we would need to
identify something else. I will use `@annotation` as a
placeholder.</div>
<div>* Also In Swift 4: the `closed` modifier is introduced.</div>
<div><br></div>
<div>* In Swift 5 the warning becomes a compiler error.
`public protocol` is not allowed. Users must use
`@annotation public protocol`.</div>
<div>* In Swift 6 `public protocol` is allowed again, now with the
new semantics. `@annotation public protocol` is also allowed,
now with a warning and a fix-it to remove the warning.</div>
<div>* In Swift 7 `@annotation public protocol` is no longer
allowed.</div>
<div><br></div>
<div>A similar mult-release strategy would work for migrating
public enums.</div>
</div>
</blockquote>
<div><br></div>
<div>A different line of feedback here:</div>
<div><br></div>
<div>As per previous reply, I now think if we clarify the mental
model of the access modifier hierarchy you're proposing and adopt
or reject with that clarity, we'll be fine whether we go with
`closed` or with `@closed`. But I don't think the source
compatibility strategy you list is the most simple or the most easy
to understand for end users.</div>
<div><br></div>
<div>- I'll leave aside closed protocols, which as per Jordan
Rose's feedback may or may not have sufficient
interestingness.</div>
<div>- With respect to enums, I don't think we need such a drastic
whiplash in terms of what will compile in future versions. Instead,
we could take a more pragmatic approach:</div>
<div><br></div>
<div>1. In Swift 4, remove the warning (or is it error?) about
`default` cases in switch statements over public enums.
Simultaneously, add `closed` or `@closed` (whatever is the approved
spelling) and start annotating standard library APIs. The
annotation will be purely future-proofing and have no functional
effect (i.e. the compiler will do nothing differently for a `closed
enum` or `@closed public enum` (as the case may be) versus a plain
`public enum`).<br></div>
<div>2. In Swift 4.1, _warn_ if switch statements over public enums
don't have a `default` statement: offer a fix-it to insert
`default: fatalError()` and, if the enum is in the same project,
offer a fix-it to insert `closed` or `@closed`.</div>
<div>3. In Swift 5, upgrade the warning to an error for
non-exhaustiveness if a switch statement over a public enum doesn't
have a `default` statement. Now, new syntax to extend an `open
enum` can be introduced and the compiler can treat closed and
public enums differently.</div>
<div><br></div>
</div>
</div>
</div>
______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
</div>
</div>
</blockquote>
</div>
<div class="m_-2179860613880217272bloop_markdown"></div>
</div>
</blockquote>
<blockquote type="cite">
<div>
<span>______________________________<wbr>_________________</span><br>
<span>swift-evolution mailing list</span><br>
<span><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a></span><br>
<span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a></span><br>
</div>
</blockquote>
</div>
</div>
</blockquote>
</div>
<div class="m_-2179860613880217272bloop_markdown"></div>
</div>
</div>
</blockquote>
</div>
</div>
</div>
<div class="m_-2179860613880217272bloop_markdown"></div>
</div>
<br>
______________________________<wbr>_________________<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" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote>
</div>
<br></div>
</div>
</div></div></span></blockquote></div><div class="bloop_markdown"><p></p></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>