> 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