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

Reply via email to