<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="">Also, note that there will be at least one other similar annotation, but for structs — the evolution document calls it @fixedContents. We want a way to declare that the set of stored properties in a struct will never change, allowing clients to make assumptions about its layout. Unlike @closed enums, @fixedContents structs mostly behave the same. The one important difference is that it will be possible to define designated initializers of @fixedContents structs inside extensions from another module.<div class=""><br class=""></div><div class="">Slava</div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Feb 12, 2017, at 8:49 AM, Matthew Johnson 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="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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=""><blockquote type="cite" class=""><div class=""><br class="Apple-interchange-newline">On Feb 12, 2017, at 10:39 AM, Nevin Brackett-Rozinsky 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="">Alternative: leave “public enum” as it is now, and spell the resilient version “@resilient enum”</div></blockquote><div class=""><br class=""></div><div class="">The problem with this approach is that the “default” is the stricter contract and library authors have to remember to add the annotation to opt-out of that stricter contract. The problems created by the stricter contract will only appear later when the author realizes they need to add new cases and now it’s a breaking change. </div><div class=""><br class=""></div><div class="">Responsible library authors should always make an intentional choice, but sometimes even the best of us make mistakes. If a library author makes this mistake it is likely that it won’t be noticed until it is too late. Requiring the library author to make a choice between mutually exclusive options rather than a choice to add or omit an annotation reduces the chance of the library author making this error. </div><div class=""><br class=""></div><div class="">This is the rationale that led to us adding `open` rather than adding something like an `@closed` annotation for classes. The desire to avoid growing lots of annotations in the language was also an important consideration that I believe applies here.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""></div><div class="">Nevin<br class=""><br class=""><br class="">On Sunday, February 12, 2017, Matthew Johnson via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div class="" style="word-wrap: break-word;"><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Feb 12, 2017, at 10:24 AM, David Hart <<a target="_blank" class="">david@hartbit.com</a>> wrote:</div><br class=""><div class=""><div dir="auto" class=""><div class=""><br class=""></div><div class="">On 12 Feb 2017, at 16:38, Matthew Johnson <<a target="_blank" class="">matthew@anandabits.com</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Feb 12, 2017, at 12:50 AM, David Hart <<a target="_blank" class="">david@hartbit.com</a>> wrote:</div><br class=""><div class=""><div dir="auto" class=""><div class=""><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">Hi Matthew,</span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"><br class=""></span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">I've read your proposal ideas and most of the discussions on the thread, and I'd like to provide some personal feedback.</span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"><br class=""></span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">Swift already has a complicated "access modifier" story so I think we really want a good reason to introduce a new one. And the problem I see is that `closed` has much less semantic weight than the other modifiers.</span></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">How so? I’m not sure if I catch your meaning here. It feels to me like it has the same semantic weight as `open`: prohibiting future versions of a module from adding cases / subclasses / conformances is roughly the inverse of lifting the restriction that clients cannot add those things. Therefore it has roughly the same degree of additional meaning over `public` as `open` does.</div></div></div></blockquote><div class=""><br class=""></div><div class="">The difference I see is precisely that 'public' and 'open' modifiers limit what the client of a module can do while closed limits what future versions of a module can do. Feels quite different to me.</div></div></div></blockquote><div class=""><br class=""></div><div class="">This is a reasonable point and is perhaps the strongest argument made against my proposal thus far. However, I think we have to consider my proposal relative to the alternatives. </div><div class=""><br class=""></div><div class="">The only alternative I am aware of is making `public enum` the resilient variety and using `@closed public enum` for the closed variety. This means that `public` will have at least two different semantics (three if we don’t reconcile classes and protocols). It also means that the resilient variety is effectively the default. I am really happy that we decide not to have a default between `open` and `public` and think the best choice is that we don’t have one here either. The fact that we have a way to do this while solving the inconsistent semantics of `public` feels like a net win to me.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><br class=""><blockquote type="cite" class=""><div class=""><div class=""><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class=""><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"><br class=""></span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">First of all, the Library Evolution document you linked says toward at the top that "this document is primarily concerned with binary compatibility, i.e. what changes can safely be made to a library between releases that will not break memory-safety or type-safety, or cause clients to fail to run at all." It seems to me that the @closed introduced in that document is much more about library resilience than about only closing down the addition of new cases: that's why it also talks about reordering and all other changes that can change the memory layout.</span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"><br class=""></span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">Swift 3 having introduced both fileprivate and open has complexified the access level story for developers and library authors. That complexity is the cost that we have paid for more expressiveness. But if we continue adding new access control modifiers to express new semantics, we may be going too far: perfect is the enemy of good.</span></div></div></div></div></blockquote><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class=""><div class=""><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"><br class="">Both of those arguments explain why I think closed should be introduced, but only as a rarely-used attribute for library authors which need to express ABI resilience, and not as an extra access modifier.</span></div></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">`closed` is about much more than binary compatibility. Any time a library publishes an enum that clients can reasonably be expected to switch statements over the library should strive to make it `closed` wherever possible. Otherwise clients are expected to handle unknown future cases by design. That is a design smell if you ask me. This means that we can expect libraries to often carefully design such enums in a way that allows them to be `closed`. The use case for resilient enums is in things like mutually exclusive option sets received as input to the module and for which it would be unusual for clients of the library to write a switch statement over.</div><div class=""><br class=""></div><div class="">With this in mind, `closed` should not be a rarely-used attribute at all. In fact it will often be the best choice. This is a big motivation behind my desire to see it on equal footing with `public` and `open`.</div><div class=""><br class=""></div><div class="">In regards to the complexity of the access model - if you look closely, `public` has three subtly different meanings today. That kind of inconsistency is part of the complexity of it. And as noted, `closed` is a concept that *will* play a significant role in Swift, regardless of how we spell it. What my proposal aims to do is to incorporate it into a consistent system of outside-the-module access modifiers. </div><div class=""><br class=""></div><div class="">One can make a very reasonable argument that access modifiers should *only* be in the business of talking about visibility and should stay out of the business of talking about “who can add to the set of cases / subclasses / conformances”. The time for that argument was when we had the `open` discussion last year. I happen to like the direction we went because it places `public` and `open` on equal footing. And now that we *have* decided to go in this direction, I think we should stick with it when we introduce `closed`.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="auto" class=""><div class=""><div class=""><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);"> </span></div><div class=""><span class="" style="background-color: rgba(255, 255, 255, 0);">David</span></div></div></div><div class=""><br class="">On 9 Feb 2017, at 00:05, Matthew Johnson via swift-evolution <<a target="_blank" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div class="">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 class=""><br class=""></div><div class=""><br class=""></div><div class="">Background and Motivation:</div><div class=""><br class=""></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 class=""><br class=""></div><div class="">A recent thread (<a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170206/031566.html" target="_blank" class="">https://lists.swift.org/<wbr class="">pipermail/swift-evolution/<wbr class="">Week-of-Mon-20170206/031566.<wbr class="">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" class="">https://github.com/apple/<wbr class="">swift/blob/master/docs/<wbr class="">LibraryEvolution.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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class=""><br class=""></div><div class="">Proposal:</div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">`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 class=""><br class=""></div><div class="">`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 class=""><br class=""></div><div class="">`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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class=""><br class=""></div><div class="">Source compatibility:</div><div class=""><br class=""></div><div class="">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 class=""><br class=""></div><div class="">Brent Royal-Gordon suggested such a strategy in a discussion regarding closed protocols on Twitter:</div><div class=""><br class=""></div><div class="">* In Swift 4: all unannotated public protocols receive a warning, possibly with a fix-it to change the annotation to `open`.</div><div class="">* 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 class="">* Also In Swift 4: the `closed` modifier is introduced.</div><div class=""><br class=""></div><div class="">* In Swift 5 the warning becomes a compiler error. `public protocol` is not allowed. Users must use `@annotation public protocol`.</div><div class="">* 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 class="">* In Swift 7 `@annotation public protocol` is no longer allowed.</div><div class=""><br class=""></div><div class="">A similar mult-release strategy would work for migrating public enums.</div><div class=""><br class=""></div><div class=""><br class=""></div></div></blockquote><blockquote type="cite" class=""><div class=""><span class="">______________________________<wbr class="">_________________</span><br class=""><span class="">swift-evolution mailing list</span><br class=""><span class=""><a target="_blank" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">evolution</a></span><br class=""></div></blockquote></div></div></blockquote></div><br class=""></div></blockquote></div></div></blockquote></div><br class=""></div></blockquote></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="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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;"><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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; float: none; display: inline !important;" class="">_______________________________________________</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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; float: none; display: inline !important;" class="">swift-evolution mailing list</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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=""><a href="mailto:swift-evolution@swift.org" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">swift-evolution@swift.org</a><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: 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-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote></div><br class=""></div></body></html>