[Draft] open and public protocols

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

Strong coupling is sometimes inevitable.

In a previous thread, I brought up an example of a place I would use this feature: Wrapping the SQLite APIs. For instance:

  public protocol SQLiteValue {
    init(statement: SQLiteStatement, columnAt index: Int) throws
    func bind(to statement: SQLiteStatement, at index: Int) throws
  }
  extension Int: SQLiteValue {
    public init(statement: SQLiteStatement, columnAt index: Int) throws {
      self = sqlite3_column_int(statement.stmt, index)
    }
    public func bind(to statement: SQLiteStatement, at index: Int) throws {
      try throwIfNotOK(
        sqlite3_bind_int64(statement.stmt, index, self)
      )
    }
  }
  extension Double: SQLiteValue {…}
  extension Data: SQLiteValue {…}
  extension String: SQLiteValue {…}
  extension Optional: SQLiteValue where Wrapped: SQLiteValue {…}

That problem is that I don’t think the API should be written this way. This cries for the use of enums instead:

enum SQLiteValue {
    case int(Int)
    case double(Double)
    case data(Data)
    case string(String)
}

protocol SQLiteValueConvertible {
    var sqliteValue: SQLiteValue { get }
}

extension Int : SQLiteValueConvertible {
    var sqliteValue: SQLiteValue { return .int(self) }
}

And that API actually allows interesting extension points. For example, how about automatic binding of dates:

extension Date : SQLiteValueConvertible {
    var sqliteValue: SQLiteValue { return .double(timeIntervalSince1970) }
}

I keep getting the impression that the uses for the proposals are actually cases where an enum is required.

···

On 20 Feb 2017, at 00:15, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 18, 2017, at 10:58 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

This is a case of your hated "strong implementation coupling". But the coupling is to a library that ultimately writes data to disk in a portable format. Strong coupling here is inevitable.

What is the purpose of permitting outside conformances to `SQLiteValue`? There is no useful way to conform to `SQLiteValue`; the underlying library supports certain types, and I've implemented support for those types. Allowing outside conformances can only mislead people into fruitlessly trying to conform their types, not realizing that the calls they need simply aren't exposed.

Moreover, exposing these details unnecessarily freezes the design of `SQLiteValue`. If I want to change the design of this parameter handling in a future version, well, too bad, the API is public, I'm stuck. *For an API I don't intend anyone to conform to publicly in the first place.* That kind of sucks, doesn't it?

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Sure. For example, maybe I have a UI widget that can display a collection of items that the user can configure. Maybe they need to be in a sequence but can only be one of a fixed set of types defined by the library (artist, album song), etc. I do not want the user to be able to add to the set of types but I do want them to be able to provide a heterogeneous collection of instances to display in the UI.

···

Sent from my iPad

On Feb 19, 2017, at 9:17 AM, David Hart <david@hartbit.com> wrote:

I still don't see the use case for this. Perhaps I'm wrong, but if an API creates a protocol for the sole purpose of representing a set of concrete types, that looks more to me like bad API design, and not a missing feature in the language. Can you give me a small concrete real-world example of an API which requires that?

On 19 Feb 2017, at 16:10, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPad

On Feb 19, 2017, at 12:58 AM, David Waite <david@alkaline-solutions.com> wrote:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

Yes. If this proposal moves ahead we can implement an API that takes a hereogenous collection with implicit conversion for several different concrete types while ensuring that all elements are of a type provided by the library:

public protocol Foo {}
public struct One: Foo {}
public struct Two: Foo {}
// other conforming types in the library

func takesFoos(_ foos: [Foo]) {}

// in user code:
takesFoos([One(), One(), Two(), One()])

This use case could be subsumed by enums if we implement some of the ideas in my value subtyping manifesto but those are for the future and may or may not be accepted. This is a relatively small change that can e made in Swift 4.

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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

Just FYI, I solved this issue in my own library (which included a json jpointer implementation) via:

public enum SubscriptParameter {
  case string(String)
  case int(Int)
}

extension SubscriptParameter : ExpressibleByIntegerLiteral {
  public init(integerLiteral value: Int) {
    self = .int(value)
  }
}

extension SubscriptParameter : ExpressibleByStringLiteral {
  public init(stringLiteral value: String) {
    self = .string(value)
  }
  public init(extendedGraphemeClusterLiteral value: String) {
    self.init(stringLiteral: value)
  }
  public init(unicodeScalarLiteral value: String) {
    self.init(stringLiteral: value)
  }
}

extension SubscriptParameter : CustomStringConvertible {
  public var description: String {
    switch self {
    case .string(let str):
      return "\"\(str)\""
    case .int(let i):
      return String(i)
    }
  }
}

func debug(_ path:SubscriptParameter...) {
  print("path is \(path)")
}

debug(1, "foo", 2, "bar”) // path is [1, “foo”, 2, “bar”]

Can you make this work with variables - not just literals - and still prevent users from extending the set of types and without requiring an enum case constructor to be used by clients?

···

Sent from my iPad

On Feb 19, 2017, at 11:29 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 19, 2017, at 1:14 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
      
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
      
    public enum SubscriptParameter {
              
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
      
    public var parameter: Document.SubscriptParameter {
          
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
      
    public var parameter: Document.SubscriptParameter {
          
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

> On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
>
> 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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

That won’t work, plus the suggested solution makes life even more complicated. I need to be able to index an array, so I’m not willing to simply give up integers and do some extra work behind the scenes and convert a String to an Int. Furthermore there is no need something like KeyConvertible to even exist.

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 13:58:35, David Hart (david@hartbit.com) schrieb:

I honestly don't see the use case. The example you showed is an example of how your circumvented the lack of "closed" protocols in Swift. But I'd really like to see an example of the necessity for closed protocols in the first place:

In your case, I would simply defined the API as:

protocol KeyConvertible {
var key: String { get }
}

public subscript(firstKey: String, parameters: KeyConvertible...) {}

Which would allow clients to implement their own KeyConvertible conforming types which convert into key paths.

On 19 Feb 2017, at 09:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
       
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
       
    public enum SubscriptParameter {
               
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Whether or not a protocol is open is also a part of API expressivity; often a library will want to expose a protocol only as a way to enable generic/existential programming over a fixed set of types, without intending that any clients add new conformers. Protocols are crucial to expressing the shared patterns amongst the set of types.

For example, I might have a TextStream protocol:

public protocol TextStream {
    func write(_: String)
}

And I might decide to expose a property as type “TextStream” purely because I reserve the right to change the underlying concrete type. I want clients to work on the protocol-existential level.

public class Component {
    var debugWriter: TextStream
}

By doing that, the underlying concrete type is no longer part of my API or ABI. I can change it between versions without breaking clients, but it’s not meaningful for any clients to create their own TextStreams; that’s not part of what my library does/allows.

- Karl

···

On 19 Feb 2017, at 16:17, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I still don't see the use case for this. Perhaps I'm wrong, but if an API creates a protocol for the sole purpose of representing a set of concrete types, that looks more to me like bad API design, and not a missing feature in the language. Can you give me a small concrete real-world example of an API which requires that?

Try to pass let intInstance = 42 in your function. ;)

That will result in .int(intInstance).

My implementation does not need that at all.

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 18:29:54, David Waite (david@alkaline-solutions.com) schrieb:

Just FYI, I solved this issue in my own library (which included a json jpointer implementation) via:

public enum SubscriptParameter {
case string(String)
case int(Int)
}

extension SubscriptParameter : ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .int(value)
}
}

extension SubscriptParameter : ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
public init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
public init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
}

extension SubscriptParameter : CustomStringConvertible {
public var description: String {
switch self {
case .string(let str):
return "\"\(str)\""
case .int(let i):
return String(i)
}
}
}

func debug(_ path:SubscriptParameter...) {
print("path is \(path)")
}

debug(1, "foo", 2, "bar”) // path is [1, “foo”, 2, “bar”]

On Feb 19, 2017, at 1:14 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
       
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
       
    public enum SubscriptParameter {
               
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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

I'll put words in Brent's mouth here as an attempt at an answer: you
sometimes need to exclusively switch a downcast of an existential.
Consider a serialization type - or SQLiteValue - where need to treat
specific fundamental types ("primitives") in implementation-dependent
ways. In a theoretical serialization design, all non-primitives would
have an explicit protocol of semantics to conform to support
serialization, much like the stdlib's "Custom" protocol pattern, and you
compose from there.

Using an enum to represent this exclusivity remains unconvincing to me,
unless we choose to add implicit conversions to the language. `.int(42)`
is an overwhelming level of duplicate type information in any practical,
non-trivial use case. Yes, (closed) enums model exclusivity. They are
not the only things to do so.

The stdlib alone is reason enough for this feature to exist, even if not
exposed publicly.

Zachary

···

On Sun, Feb 19, 2017, at 06:40 PM, Xiaodi Wu via swift-evolution wrote:

On Sun, Feb 19, 2017 at 5:15 PM, Brent Royal-Gordon via swift- > evolution <swift-evolution@swift.org> wrote:

What is the harm of permitting an outside conformance to
`SQLiteValue`?

It’s funny. My favourites SQLite library actually does it as I suggested :)

···

On 20 Feb 2017, at 00:34, David Hart <david@hartbit.com> wrote:

On 20 Feb 2017, at 00:15, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 18, 2017, at 10:58 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

Strong coupling is sometimes inevitable.

In a previous thread, I brought up an example of a place I would use this feature: Wrapping the SQLite APIs. For instance:

  public protocol SQLiteValue {
    init(statement: SQLiteStatement, columnAt index: Int) throws
    func bind(to statement: SQLiteStatement, at index: Int) throws
  }
  extension Int: SQLiteValue {
    public init(statement: SQLiteStatement, columnAt index: Int) throws {
      self = sqlite3_column_int(statement.stmt, index)
    }
    public func bind(to statement: SQLiteStatement, at index: Int) throws {
      try throwIfNotOK(
        sqlite3_bind_int64(statement.stmt, index, self)
      )
    }
  }
  extension Double: SQLiteValue {…}
  extension Data: SQLiteValue {…}
  extension String: SQLiteValue {…}
  extension Optional: SQLiteValue where Wrapped: SQLiteValue {…}

That problem is that I don’t think the API should be written this way. This cries for the use of enums instead:

enum SQLiteValue {
   case int(Int)
   case double(Double)
   case data(Data)
   case string(String)
}

protocol SQLiteValueConvertible {
   var sqliteValue: SQLiteValue { get }
}

extension Int : SQLiteValueConvertible {
   var sqliteValue: SQLiteValue { return .int(self) }
}

And that API actually allows interesting extension points. For example, how about automatic binding of dates:

extension Date : SQLiteValueConvertible {
   var sqliteValue: SQLiteValue { return .double(timeIntervalSince1970) }
}

I keep getting the impression that the uses for the proposals are actually cases where an enum is required.

This is a case of your hated "strong implementation coupling". But the coupling is to a library that ultimately writes data to disk in a portable format. Strong coupling here is inevitable.

What is the purpose of permitting outside conformances to `SQLiteValue`? There is no useful way to conform to `SQLiteValue`; the underlying library supports certain types, and I've implemented support for those types. Allowing outside conformances can only mislead people into fruitlessly trying to conform their types, not realizing that the calls they need simply aren't exposed.

Moreover, exposing these details unnecessarily freezes the design of `SQLiteValue`. If I want to change the design of this parameter handling in a future version, well, too bad, the API is public, I'm stuck. *For an API I don't intend anyone to conform to publicly in the first place.* That kind of sucks, doesn't it?

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

IMHO the existence of open as an _exclusive_ access modifier is not worth. If people not going with the consistent idea of open vs. public then open should be removed completely, then public should be reverted to what it meant before. Afterwards we would introduce closed as an extra attribute. open class would be equivalent to public class, where today public class would become closed public class. That would be a little more consistent to final public behavior.

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 14:02:54, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

That won’t work, plus the suggested solution makes life even more complicated. I need to be able to index an array, so I’m not willing to simply give up integers and do some extra work behind the scenes and convert a String to an Int. Furthermore there is no need something like KeyConvertible to even exist.

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 13:58:35, David Hart (david@hartbit.com) schrieb:

I honestly don't see the use case. The example you showed is an example of how your circumvented the lack of "closed" protocols in Swift. But I'd really like to see an example of the necessity for closed protocols in the first place:

In your case, I would simply defined the API as:

protocol KeyConvertible {
var key: String { get }
}

public subscript(firstKey: String, parameters: KeyConvertible...) {}

Which would allow clients to implement their own KeyConvertible conforming types which convert into key paths.

On 19 Feb 2017, at 09:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
        
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
        
    public enum SubscriptParameter {
                
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
        
    public var parameter: Document.SubscriptParameter {
            
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
        
    public var parameter: Document.SubscriptParameter {
            
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I'm sorry but I'm really lost: in your example, how could the client break the library by providing a custom type that conforms to the protocol? Why do you say that something like KeyConvertible shouldn't even exist? They exist so clients can extend libraires and make them even more useful. I still haven't seen a case where it's necessary to avoid clients to conform to a protocol.

···

On 19 Feb 2017, at 14:02, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

That won’t work, plus the suggested solution makes life even more complicated. I need to be able to index an array, so I’m not willing to simply give up integers and do some extra work behind the scenes and convert a String to an Int. Furthermore there is no need something like KeyConvertible to even exist.

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 13:58:35, David Hart (david@hartbit.com) schrieb:

I honestly don't see the use case. The example you showed is an example of how your circumvented the lack of "closed" protocols in Swift. But I'd really like to see an example of the necessity for closed protocols in the first place:

In your case, I would simply defined the API as:

protocol KeyConvertible {
    var key: String { get }
}

public subscript(firstKey: String, parameters: KeyConvertible...) {}

Which would allow clients to implement their own KeyConvertible conforming types which convert into key paths.

On 19 Feb 2017, at 09:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
       
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
       
    public enum SubscriptParameter {
               
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

> On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:
>
> 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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

@Matthew: Have you considered what happens with the access modifier of an open protocol when an open/public class conforms to it?

// Module A
open protocol A {
    func foo()
}

// Module B
open class B : A {
    (open or public) func foo() {}
    // If `open`, can we downgrade to `public`?
    // The other way around seems straightforward
}

This makes more sense already. But in this case, wouldn't I be interested, as a client, in creating my own TextStreams, some of which might write to a custom log file, to a web service, etc... ?

···

On 19 Feb 2017, at 17:00, Karl Wagner <razielim@gmail.com> wrote:

On 19 Feb 2017, at 16:17, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

I still don't see the use case for this. Perhaps I'm wrong, but if an API creates a protocol for the sole purpose of representing a set of concrete types, that looks more to me like bad API design, and not a missing feature in the language. Can you give me a small concrete real-world example of an API which requires that?

Whether or not a protocol is open is also a part of API expressivity; often a library will want to expose a protocol only as a way to enable generic/existential programming over a fixed set of types, without intending that any clients add new conformers. Protocols are crucial to expressing the shared patterns amongst the set of types.

For example, I might have a TextStream protocol:

public protocol TextStream {
    func write(_: String)
}

And I might decide to expose a property as type “TextStream” purely because I reserve the right to change the underlying concrete type. I want clients to work on the protocol-existential level.

public class Component {
    var debugWriter: TextStream
}

By doing that, the underlying concrete type is no longer part of my API or ABI. I can change it between versions without breaking clients, but it’s not meaningful for any clients to create their own TextStreams; that’s not part of what my library does/allows.

- Karl

That’s the whole point I was making. :) Thank you Matthew. That makes my example a real world example and not just some bike shedding.

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 18:50:31, Matthew Johnson (matthew@anandabits.com) schrieb:

Sent from my iPad

On Feb 19, 2017, at 11:29 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

Just FYI, I solved this issue in my own library (which included a json jpointer implementation) via:

public enum SubscriptParameter {
case string(String)
case int(Int)
}

extension SubscriptParameter : ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .int(value)
}
}

extension SubscriptParameter : ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
public init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
public init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
}

extension SubscriptParameter : CustomStringConvertible {
public var description: String {
switch self {
case .string(let str):
return "\"\(str)\""
case .int(let i):
return String(i)
}
}
}

func debug(_ path:SubscriptParameter...) {
print("path is \(path)")
}

debug(1, "foo", 2, "bar”) // path is [1, “foo”, 2, “bar”]

Can you make this work with variables - not just literals - and still prevent users from extending the set of types and without requiring an enum case constructor to be used by clients?

On Feb 19, 2017, at 1:14 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
       
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
       
    public enum SubscriptParameter {
               
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
       
    public var parameter: Document.SubscriptParameter {
           
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Left unsaid from my reply about enums is that implicit conversions should
absolutely be added. We already have this magic for one particular enum,
Optional.

I'm not arguing with you that enums are currently unsuitable. In fact I
entirely agree. But Chris Lattner and others have said (now I'm
paraphrasing, but I believe accurately) that they *should* be. What will it
take? At minimum, some way to opt into implicit conversions. It's a
no-brainer in my mind.

Bottom line: the language needs one excellent way to model Foo | Bar | Baz,
not two or three mediocre workarounds. The core team has said that they
want that excellence to be built through enums. Let's do it.

···

On Sun, Feb 19, 2017 at 21:28 Zach Waldowski via swift-evolution < swift-evolution@swift.org> wrote:

On Sun, Feb 19, 2017, at 06:40 PM, Xiaodi Wu via swift-evolution wrote:

On Sun, Feb 19, 2017 at 5:15 PM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

What is the harm of permitting an outside conformance to `SQLiteValue`?

I'll put words in Brent's mouth here as an attempt at an answer: you
sometimes need to exclusively switch a downcast of an existential. Consider
a serialization type - or SQLiteValue - where need to treat specific
fundamental types ("primitives") in implementation-dependent ways. In a
theoretical serialization design, all non-primitives would have an explicit
protocol of semantics to conform to support serialization, much like the
stdlib's "Custom" protocol pattern, and you compose from there.

Using an enum to represent this exclusivity remains unconvincing to me,
unless we choose to add implicit conversions to the language. `.int(42)` is
an overwhelming level of duplicate type information in any practical,
non-trivial use case. Yes, (closed) enums model exclusivity. They are not
the only things to do so.

The stdlib alone is reason enough for this feature to exist, even if not
exposed publicly.

Zachary

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

@Matthew: Have you considered what happens with the access modifier of an open protocol when an open/public class conforms to it?

Yes I covered this in the detailed design section of the proposal.

···

Sent from my iPhone

On Feb 19, 2017, at 10:11 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:
// Module A
open protocol A {
    func foo()
}

// Module B
open class B : A {
    (open or public) func foo() {}
    // If `open`, can we downgrade to `public`?
    // The other way around seems straightforward
}

Sorry, I have read through this thread twice and do not understand the
point you are making. Can you explain your example once more?

Specifically, I do not understand why it is that your code should have
problems with third-party types that conform to your protocol. If your
protocol has requirements that third-party types cannot fulfill, then
naturally those requirements will prevent a third-party from conforming new
types to your protocol. OTOH, if your protocol does not have any
requirements that third-party types cannot fulfill, then your code that
accepts anything that conforms to your protocol should be indifferent to
whether the conforming type is defined by you or someone else. After all, a
conforming type would fulfill all the semantic and syntactic requirements
of the protocol! If your protocol has requirements that you are unable to
state in code, causing third-party types to conform that really shouldn't,
then that is a case for additional features that allow you to express those
requirements more accurately, not an argument for having protocols that
can't be conformed to by a third-party. What am I missing?

···

On Sun, Feb 19, 2017 at 11:53 AM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

That’s the whole point I was making. :) Thank you Matthew. That makes my
example a real world example and not just some bike shedding.

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 18:50:31, Matthew Johnson (matthew@anandabits.com)
schrieb:

Sent from my iPad

On Feb 19, 2017, at 11:29 AM, David Waite via swift-evolution < > swift-evolution@swift.org> wrote:

Just FYI, I solved this issue in my own library (which included a json
jpointer implementation) via:

public enum SubscriptParameter {
  case string(String)
  case int(Int)
}

extension SubscriptParameter : ExpressibleByIntegerLiteral {
  public init(integerLiteral value: Int) {
    self = .int(value)
  }
}

extension SubscriptParameter : ExpressibleByStringLiteral {
  public init(stringLiteral value: String) {
    self = .string(value)
  }
  public init(extendedGraphemeClusterLiteral value: String) {
    self.init(stringLiteral: value)
  }

  public init(unicodeScalarLiteral value: String) {
    self.init(stringLiteral: value)
  }
}

extension SubscriptParameter : CustomStringConvertible {
  public var description: String {
    switch self {
    case .string(let str):
      return "\"\(str)\""
    case .int(let i):
      return String(i)
    }
  }
}

func debug(_ path:SubscriptParameter...) {
  print("path is \(path)")
}

debug(1, "foo", 2, "bar”) // path is [1, “foo”, 2, “bar”]

Can you make this work with variables - not just literals - and still
prevent users from extending the set of types and without requiring an enum
case constructor to be used by clients?

On Feb 19, 2017, at 1:14 AM, Adrian Zubarev <adrian.zubarev@devandartist. > > wrote:

If you haven’t followed the other thread Matthew previously opened than
you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {

    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {

    public enum SubscriptParameter {

        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {

    public var parameter: Document.SubscriptParameter {

        return .string(self)
    }
}

extension Int : SubscriptParameterType {

    public var parameter: Document.SubscriptParameter {

        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }

The absence of closed protocols forced me to create a special requirement
on that protocol to prevent the client from conforming to that protocol and
passing instances of other types my API wouldn’t want to deal with. That
creates unnecessary copies and I need to unpack the enum payload to find
out which type the user passed. Instead I could simply close the protocol,
wouldn’t need the requirement to exist and I could simply cast the type to
String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].
------------------------------

Here is a list of hidden and semi-hidden protocols from the standard
library that could be closed. Formatted version: https://gist.github.
com/DevAndArtist/168c800d784829be536c407311953ab7
Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _
HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21
_BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _
ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _
ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _
ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _
ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _
ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _
ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _
ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _
ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709
_ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720
_ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _
ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750
_DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108
_DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33
_IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLo
okable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20
_RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27
_RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353
_ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456
_BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (
swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world
use for this which isn’t just hiding strong implementation coupling behind
a protocol?

When I consume a protocol, it is under the assumption that the protocol is
documented such that I would be able to work against *any* implementation
of the protocol. With a closed protocol, I would have to assume that there
are significant side effects, either undocumented or difficult for a third
party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol
(which itself can be a sign that your design isn’t properly using
polymorphism), one could get this design by creating a struct with the
interface desired, and passing invocations through to an internal protocol
reference.

-DW

> On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:
>
> 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

_______________________________________________
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

In my case we don’t have an Either type, nor do we allow implicit type conversions. Remove the property from my protocol and forget about the enum that I used there. Conform to the protocol in your app a type different from String or Int and call the subscript. This will result in a runtime crash, because the API won’t know what do to with your type. Sure it could return nil, but then I can only say “happy debugging to find your wrong type”.

The usage of the protocol is simply an implementation artifact, because there is no other way (except crazy long overloads) of creating such a subscript that accepts a String or an Int from the second parameter. The protocol was never meant to be conformable to outside the API itself.

The requirement that the protocol has in my example was the workaround to prevent the crash and iff you device to conform to that protocol, which, as I already mentioned, is not intended at all.

I can only repeat myself: if you haven’t bumped into such an issue, than call yourself lucky.

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 16:05:28, David Hart (david@hartbit.com) schrieb:

I'm sorry but I'm really lost: in your example, how could the client break the library by providing a custom type that conforms to the protocol? Why do you say that something like KeyConvertible shouldn't even exist? They exist so clients can extend libraires and make them even more useful. I still haven't seen a case where it's necessary to avoid clients to conform to a protocol.

On 19 Feb 2017, at 14:02, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

That won’t work, plus the suggested solution makes life even more complicated. I need to be able to index an array, so I’m not willing to simply give up integers and do some extra work behind the scenes and convert a String to an Int. Furthermore there is no need something like KeyConvertible to even exist.

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 13:58:35, David Hart (david@hartbit.com) schrieb:

I honestly don't see the use case. The example you showed is an example of how your circumvented the lack of "closed" protocols in Swift. But I'd really like to see an example of the necessity for closed protocols in the first place:

In your case, I would simply defined the API as:

protocol KeyConvertible {
var key: String { get }
}

public subscript(firstKey: String, parameters: KeyConvertible...) {}

Which would allow clients to implement their own KeyConvertible conforming types which convert into key paths.

On 19 Feb 2017, at 09:14, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

If you haven’t followed the other thread Matthew previously opened than you have missed the example I showed there.

Here it is again:

public protocol SubscriptParameterType {
        
    // This property was needed to prevent the client from breaking
    // the library by conforming to the protocol, but I'd like to
    // keep it invisible for the client, or even better prevent the
    // client from conforming to the protocol.
    var parameter: Document.SubscriptParameter { get }
}

extension Document {
        
    public enum SubscriptParameter {
                
        case string(String)
        case integer(Int)
    }
}

extension String : SubscriptParameterType {
        
    public var parameter: Document.SubscriptParameter {
            
        return .string(self)
    }
}

extension Int : SubscriptParameterType {
        
    public var parameter: Document.SubscriptParameter {
            
        return .integer(self)
    }
}

// Somewhere inside the `Document` type
public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … }
The absence of closed protocols forced me to create a special requirement on that protocol to prevent the client from conforming to that protocol and passing instances of other types my API wouldn’t want to deal with. That creates unnecessary copies and I need to unpack the enum payload to find out which type the user passed. Instead I could simply close the protocol, wouldn’t need the requirement to exist and I could simply cast the type to String or Int when needed.

That implementation enables more safe queries of my Document type like

document["key1", intIndexInstance, stringKeyInstance, 10, "key"]

rather than

document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key"].

Here is a list of hidden and semi-hidden protocols from the standard library that could be closed. Formatted version: swift-closed-protocols.md · GitHub

Path Protocol
/swift/stdlib/public/core/AnyHashable.swift:16 _HasCustomAnyHashableRepresentation
/swift/stdlib/public/core/BidirectionalCollection.swift:21 _BidirectionalIndexable
/swift/stdlib/public/core/BridgeObjectiveC.swift:19 _ObjectiveCBridgeable
/swift/stdlib/public/core/Collection.swift:20 _IndexableBase
/swift/stdlib/public/core/Collection.swift:176 _Indexable
/swift/stdlib/public/core/CompilerProtocols.swift:193 _ExpressibleByBuiltinIntegerLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:240 _ExpressibleByBuiltinFloatLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:283 _ExpressibleByBuiltinBooleanLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:316 _ExpressibleByBuiltinUnicodeScalarLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:350 _ExpressibleByBuiltinExtendedGraphemeClusterLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:398 _ExpressibleByBuiltinStringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:407 _ExpressibleByBuiltinUTF16StringLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:670 _ExpressibleByStringInterpolation
/swift/stdlib/public/core/CompilerProtocols.swift:709 _ExpressibleByColorLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:720 _ExpressibleByImageLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:730 _ExpressibleByFileReferenceLiteral
/swift/stdlib/public/core/CompilerProtocols.swift:750 _DestructorSafeContainer
/swift/stdlib/public/core/FixedPoint.swift.gyb:53 _Integer
/swift/stdlib/public/core/FixedPoint.swift.gyb:70 _SignedInteger
/swift/stdlib/public/core/FixedPoint.swift.gyb:108 _DisallowMixedSignArithmetic
/swift/stdlib/public/core/Hashable.swift:16 _Hashable
/swift/stdlib/public/core/Index.swift:16 _Incrementable
/swift/stdlib/public/core/IntegerArithmetic.swift.gyb:33 _IntegerArithmetic
/swift/stdlib/public/core/Mirror.swift:721 _DefaultCustomPlaygroundQuickLookable
/swift/stdlib/public/core/MutableCollection.swift:20 _MutableIndexable
/swift/stdlib/public/core/NewtypeWrapper.swift.gyb:16 _SwiftNewtypeWrapper
/swift/stdlib/public/core/Pointer.swift:16 _Pointer
/swift/stdlib/public/core/RandomAccessCollection.swift:20 _RandomAccessIndexable
/swift/stdlib/public/core/RangeReplaceableCollection.swift.gyb:27 _RangeReplaceableIndexable
/swift/stdlib/public/core/ReflectionLegacy.swift:41 _Mirror
/swift/stdlib/public/core/ShadowProtocols.swift:27 _ShadowProtocol
/swift/stdlib/public/core/ShadowProtocols.swift:31 _NSFastEnumeration
/swift/stdlib/public/core/ShadowProtocols.swift:41 _NSEnumerator
/swift/stdlib/public/core/ShadowProtocols.swift:51 _NSCopying
/swift/stdlib/public/core/ShadowProtocols.swift:61 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:83 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:125 _NSDictionary
/swift/stdlib/public/core/ShadowProtocols.swift:137 _NSSetCore
/swift/stdlib/public/core/ShadowProtocols.swift:171 _NSSet
/swift/stdlib/public/core/ShadowProtocols.swift:177 _NSNumber
/swift/stdlib/public/core/ShadowProtocols.swift:187 _NSArrayCore
/swift/stdlib/public/core/ShadowProtocols.swift:188 _NSDictionaryCore
/swift/stdlib/public/core/ShadowProtocols.swift:189 _NSSetCore
/swift/stdlib/public/core/StringBridge.swift:194 _NSStringCore
/swift/stdlib/public/SDK/Foundation/NSError.swift:353 _ObjectiveCBridgeableError
/swift/stdlib/public/SDK/Foundation/NSError.swift:379 __BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:446 _BridgedNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:456 _BridgedStoredNSError
/swift/stdlib/public/SDK/Foundation/NSError.swift:564 _ErrorCodeProtocol

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 07:59:45, David Waite via swift-evolution (swift-evolution@swift.org) schrieb:

I am unsure if this feature is a good idea. Does someone have a real-world use for this which isn’t just hiding strong implementation coupling behind a protocol?

When I consume a protocol, it is under the assumption that the protocol is documented such that I would be able to work against *any* implementation of the protocol. With a closed protocol, I would have to assume that there are significant side effects, either undocumented or difficult for a third party to duplicate. To my experience, that sounds brittle.

Assuming you aren’t switching on the implementing type of a protocol (which itself can be a sign that your design isn’t properly using polymorphism), one could get this design by creating a struct with the interface desired, and passing invocations through to an internal protocol reference.

-DW

On Feb 18, 2017, at 1:41 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

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:

// 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:

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:

// 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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I really feel I’m blind, I cannot find it. Is the access modifier of open protocol *members* on open/public classes public by default, or open?

If open, can we downgrade it to public in an open class?

···

--
Adrian Zubarev
Sent with Airmail

Am 19. Februar 2017 um 17:16:59, Matthew Johnson (matthew@anandabits.com) schrieb:

Sent from my iPhone

On Feb 19, 2017, at 10:11 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

@Matthew: Have you considered what happens with the access modifier of an open protocol when an open/public class conforms to it?

Yes I covered this in the detailed design section of the proposal.
// Module A
open protocol A {
    func foo()
}

// Module B
open class B : A {
    (open or public) func foo() {}
    // If `open`, can we downgrade to `public`?
    // The other way around seems straightforward
}

Perhaps, but there are times when you don’t want that, or when accommodating custom instances is not so straightforward. The debugWriter instance is only _exposed_ as a “TextStream”; but there may be more (internal) requirements on the underlying instance - e.g. that it is Flushable, InitialisableFromNativeFileObject, or whatever. I would rather not expose them all, and just expose the instance as “something which is a TextStream, so you can call TextStream methods on it”, if I decide that’s all they need to know.

- Karl

···

On 19 Feb 2017, at 17:36, David Hart <david@hartbit.com> wrote:

This makes more sense already. But in this case, wouldn't I be interested, as a client, in creating my own TextStreams, some of which might write to a custom log file, to a web service, etc... ?

On 19 Feb 2017, at 17:00, Karl Wagner <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 19 Feb 2017, at 16:17, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I still don't see the use case for this. Perhaps I'm wrong, but if an API creates a protocol for the sole purpose of representing a set of concrete types, that looks more to me like bad API design, and not a missing feature in the language. Can you give me a small concrete real-world example of an API which requires that?

Whether or not a protocol is open is also a part of API expressivity; often a library will want to expose a protocol only as a way to enable generic/existential programming over a fixed set of types, without intending that any clients add new conformers. Protocols are crucial to expressing the shared patterns amongst the set of types.

For example, I might have a TextStream protocol:

public protocol TextStream {
    func write(_: String)
}

And I might decide to expose a property as type “TextStream” purely because I reserve the right to change the underlying concrete type. I want clients to work on the protocol-existential level.

public class Component {
    var debugWriter: TextStream
}

By doing that, the underlying concrete type is no longer part of my API or ABI. I can change it between versions without breaking clients, but it’s not meaningful for any clients to create their own TextStreams; that’s not part of what my library does/allows.

- Karl

Left unsaid from my reply about enums is that implicit conversions should absolutely be added. We already have this magic for one particular enum, Optional.

+1. I cover a design to enable this in my value subtyping manifesto.

I'm not arguing with you that enums are currently unsuitable. In fact I entirely agree. But Chris Lattner and others have said (now I'm paraphrasing, but I believe accurately) that they *should* be. What will it take? At minimum, some way to opt into implicit conversions. It's a no-brainer in my mind.

Bottom line: the language needs one excellent way to model Foo | Bar | Baz, not two or three mediocre workarounds. The core team has said that they want that excellence to be built through enums. Let's do it.

I would love to hear your thoughts on my value subtyping manifesto which covers a plethora of enum enhancements, including everything relevant to and discussed in this thread.

That said, there are cases where enums are not appropriate and a closed protocol is more appropriate. Specifically when the set of types is expected to evolve over time or is large enough that an enum is a bit unwieldy, but is still intended to be controlled by the library. In these cases you may well prefer to use polymorphism rather than switch statements in the implementation.

I see no reason to have "one true way". Each approach has pros and cons. An engineering tradeoff is required. The fact that one solution may be the most appropriate in the majority of cases is no reason to preclude the alternative from being available. It may be the better choice in a nontrivial minority of cases, which is plenty frequently enough to justify its availability.

···

Sent from my iPad

On Feb 19, 2017, at 9:57 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Feb 19, 2017 at 21:28 Zach Waldowski via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Feb 19, 2017, at 06:40 PM, Xiaodi Wu via swift-evolution wrote:

On Sun, Feb 19, 2017 at 5:15 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
What is the harm of permitting an outside conformance to `SQLiteValue`?

I'll put words in Brent's mouth here as an attempt at an answer: you sometimes need to exclusively switch a downcast of an existential. Consider a serialization type - or SQLiteValue - where need to treat specific fundamental types ("primitives") in implementation-dependent ways. In a theoretical serialization design, all non-primitives would have an explicit protocol of semantics to conform to support serialization, much like the stdlib's "Custom" protocol pattern, and you compose from there.

Using an enum to represent this exclusivity remains unconvincing to me, unless we choose to add implicit conversions to the language. `.int(42)` is an overwhelming level of duplicate type information in any practical, non-trivial use case. Yes, (closed) enums model exclusivity. They are not the only things to do so.

The stdlib alone is reason enough for this feature to exist, even if not exposed publicly.

Zachary

_______________________________________________
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