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