Great proposal Matthew, huge thanks for pushing this forward and putting it 
together. I simply cannot wait for this to be implemented. :)

+1

I spotted two typos while reading:

public protocol P P{} - one P too much
There is somewhere an erro without an r at the end


-- 
Adrian Zubarev
Sent with Airmail

Am 18. Februar 2017 um 21:42:41, Matthew Johnson via swift-evolution 
(swift-evolution@swift.org) schrieb:

Now that we’re in phase 2 I’d like to officially propose we introduce `open` 
protocols and require conformances to `public` protocols be inside the 
declaring module. Let’s use this thread for feedback on the official proposal. 
After a healthy round of discussion I’ll open a PR to submit it for review.


# Feature name

* Proposal: [SE-NNNN](NNNN-open-public-protocols.md)
* Authors: [Matthew Johnson](https://github.com/anandabits)
* Review Manager: TBD
* Status: **Awaiting review**

## Introduction

This proposal introduces `open protocol` and changes the meaning of `public 
protocol` to match the meaning of `public class` (in this case, conformances 
are only allowed inside the declaring module).

The pitch thread leading up to this proposal was: [consistent public access 
modifiers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170206/031653.html)

## Motivation

A general principle the Swift community has adopted for access control is that 
defaults should reserve maximum flexibility for a library. The ensures that any 
capabilities beyond mere visibility are not available unless the author of the 
library has explicitly declared their intent that the capabilities be made 
available. Finally, when it is possible to switch from one semantic to another 
without breaking clients (but not vice-versa) we should prefer the more 
forgiving (i.e. fixable) semantic as the (soft) default.

`public` is considered a "soft default" in the sense that it is the first 
access modifier a user will reach for when exposing a declaration outside of 
the module. In the case of protocols the current meaning of `public` does not 
meet the principle of preserving maximum flexibility for the author of the 
library. It allows users of the library to conform to the protocol.

There are good reasons a library may not wish to allow users to add 
conformances to a protocol. For example, it may not wish to expose the 
conforming concrete types. While similar behavior could be accomplished with an 
enum if cases could be private, that requires an implementation to use switch 
statements rather than polymorphism.

Even if all the conforming types are also public there are cases where 
polymorphism is the preferred implementation. For example, if the set of 
conforming types is not expected to be fixed (despite all being inside the 
library) the authors may not want to have to maintain switch statements every 
time they need to add or remove a confroming type which would be necessary if 
an enum were used instead. Polymorphism allows us to avoid this, giving us the 
ability to add and remove conforming types within the implementation of the 
library without the burden of maintaining switch statements.

Aligning the access modifiers for protocols and classes allows us to specify 
both conformable and non-conformable protocols, provides a soft default that is 
consistent with the principle of (soft) defaults reserving maximum flexibility 
for the library, and increases the overall consistency of the language by 
aligning the semantics of access control for protocols and classes.

The standard library currently has at least one protocol (`MirrorPath`) that is 
documented as disallowing client conformances. If this proposal is adopted it 
is likely that `MirrorPath` would be declared `public protocol` and not `open 
protocol`.

Jordan Rose has indicated that the Apple frameworks also include a number of 
protocols documented with the intent that users do not add conformances. 
Perhaps an importer annotation would allow the compiler to enforce these 
semantics in Swift code as well.

## Proposed solution

The proposed solution is to change the meaning of `public protocol` to disallow 
conformances outside the declaring module and introduce `open protocol` to 
allow conformances outside the decalring module (equivalent to the current 
meaning of `public protocol`).

## Detailed design

The detailed design is relatively straightforward but there are three important 
wrinkles to consider.

### User refinement of public protocols

Consider the following example:

```swift
// Library module:
public protocol P {}
public class C: P {}

// User module:
protocol User: P {}
extension C: User {}
```

The user module is allowed to add a refinement to `P` because this does not 
have any impact on the impelementation of the library or its possible 
evolution. It simply allows the user to write code that is generic over a 
subset of the conforming types provided by the library.

### Public protocols with open conforming classes

Consider the following example:

```swift
public protocol P P{}
open class C: P {}
```

Users of this module will be able to add subclasses of `C` that have a 
conformance to `P`. This is allowed becuase the client of the module did not 
need to explicitly declare a conformance and the module has explicitly stated 
its intent to allow subclasses of `C` with the `open` access modifier.

### Open protocols that refine public protocols

Consider the following example:

```swift
// library module:
public protocol P {}
open protocol Q: P {}
open protocol R: P {}

// user module:
struct S: P {} // error `P` is not `open`
struct T: Q {} // ok
struct U: R {} // ok
```

The user module is allowed to introudce a conformance to `P`, but only 
indirectly by also conforming to `Q`. The meaning we have ascribed to the 
keywords implies that this should be allowed and it offers libraries a very 
wide design space from which to choose. The library is able to have types that 
conform directly to `P`, while placing additional requirements on user types if 
necessary.

## Source compatibility

This proposal breaks source compatibility, but in a way that allows for a 
simple mechanical migration. A multi-release stratgegy will be used to roll out 
this proposal to provide maximum possible source compatibility from one release 
to the next.

1. In Swift 4, introduce the `open` keyword and the `@nonopen` attribute (which 
can be applied to `public protocol` to give it the new semantics of `public`).
2. In Swift 4 (or 4.1 if necessary) start warning for `public protocol` with no 
annotation.
3. In the subsequent release `public protocol` without annotation becomes an 
error.
4. In the subsequent relase `public protocol` without annotation takes on the 
new semantics.
5. `@nonopen` becomes a warning, and evenutally an erro as soon as we are 
comfortable making those changes.

## Effect on ABI stability

I would appreciate it if others can offer input regarding this section. I 
believe this proposal has ABI consequences, but it's possible that it could be 
an additivie ABI change where the ABI for conformable protocols remains the 
same and we add ABI for non-conformable protocols later. If that is possible, 
the primary impact would be the ABI of any standard library protocols that 
would prefer to be non-conformable.

## Effect on API resilience

This proposal would may impact one or more protocols in the standard library, 
such as `MirrorPath`, which would likely choose to remain `public` rather than 
adopt `open`.

## Alternatives considered

The primary alternatives are to either make no change, or to add something like 
`closed protocol`. The issues motivating the current proposal as a better 
alternative than either of these options are covered in the motivation section.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
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