> On 19 Feb 2017, at 23:24, Xiaodi Wu via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> 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?

Yeah, sums up my opinion pretty accurately.

> On Sun, Feb 19, 2017 at 11:53 AM, Adrian Zubarev via swift-evolution 
> <swift-evolution@swift.org <mailto: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 (matt...@anandabits.com 
> <mailto:matt...@anandabits.com>) schrieb:
> 
>> 
>> 
>> Sent from my iPad
>> 
>> On Feb 19, 2017, at 11:29 AM, David Waite via swift-evolution 
>> <swift-evolution@swift.org <mailto: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.zuba...@devandartist.com <mailto:adrian.zuba...@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: 
>>>> https://gist.github.com/DevAndArtist/168c800d784829be536c407311953ab7 
>>>> <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 
>>>> _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 <mailto: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 <mailto: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 
>>>>> > <http://protocols.md/>)
>>>>> > * Authors: [Matthew Johnson](https://github.com/anandabits 
>>>>> > <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
>>>>> >  
>>>>> > <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 <mailto:swift-evolution@swift.org>
>>>>> > https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>> 
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

Reply via email to