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.
Slava > On Feb 12, 2017, at 8:49 AM, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote: > >> >> On Feb 12, 2017, at 10:39 AM, Nevin Brackett-Rozinsky via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> Alternative: leave “public enum” as it is now, and spell the resilient >> version “@resilient enum” > > 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. > > 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. > > 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. > >> >> Nevin >> >> >> On Sunday, February 12, 2017, Matthew Johnson via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >>> On Feb 12, 2017, at 10:24 AM, David Hart <da...@hartbit.com <>> wrote: >>> >>> >>> On 12 Feb 2017, at 16:38, Matthew Johnson <matt...@anandabits.com <>> wrote: >>> >>>> >>>>> On Feb 12, 2017, at 12:50 AM, David Hart <da...@hartbit.com <>> wrote: >>>>> >>>>> Hi Matthew, >>>>> >>>>> I've read your proposal ideas and most of the discussions on the thread, >>>>> and I'd like to provide some personal feedback. >>>>> >>>>> 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. >>>> >>>> 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. >>> >>> 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. >> >> 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. >> >> 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. >> >>> >>>>> >>>>> 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. >>>>> >>>>> 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. >>>>> >>>>> 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. >>>> >>>> `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. >>>> >>>> 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`. >>>> >>>> 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. >>>> >>>> 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`. >>>> >>>>> >>>>> David >>>>> >>>>> On 9 Feb 2017, at 00:05, Matthew Johnson via swift-evolution >>>>> <swift-evolution@swift.org <>> wrote: >>>>> >>>>>> 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. >>>>>> >>>>>> >>>>>> Background and Motivation: >>>>>> >>>>>> 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). >>>>>> >>>>>> A recent thread >>>>>> (https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170206/031566.html >>>>>> >>>>>> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170206/031566.html>) >>>>>> 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 >>>>>> (https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums >>>>>> >>>>>> <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums>) >>>>>> 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. >>>>>> >>>>>> 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. >>>>>> >>>>>> 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. >>>>>> >>>>>> 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. >>>>>> >>>>>> 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. >>>>>> >>>>>> 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. >>>>>> >>>>>> >>>>>> Proposal: >>>>>> >>>>>> 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. >>>>>> >>>>>> `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) >>>>>> >>>>>> `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. >>>>>> >>>>>> `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. >>>>>> >>>>>> 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`. >>>>>> >>>>>> 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. >>>>>> >>>>>> >>>>>> Source compatibility: >>>>>> >>>>>> 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. >>>>>> >>>>>> Brent Royal-Gordon suggested such a strategy in a discussion regarding >>>>>> closed protocols on Twitter: >>>>>> >>>>>> * In Swift 4: all unannotated public protocols receive a warning, >>>>>> possibly with a fix-it to change the annotation to `open`. >>>>>> * 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. >>>>>> * Also In Swift 4: the `closed` modifier is introduced. >>>>>> >>>>>> * In Swift 5 the warning becomes a compiler error. `public protocol` is >>>>>> not allowed. Users must use `@annotation public protocol`. >>>>>> * 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. >>>>>> * In Swift 7 `@annotation public protocol` is no longer allowed. >>>>>> >>>>>> A similar mult-release strategy would work for migrating public enums. >>>>>> >>>>>> >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> swift-evolution@swift.org <> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>> >> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >> https://lists.swift.org/mailman/listinfo/swift-evolution > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org <mailto:swift-evolution@swift.org> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution