Sorry if that sounds a little bit rude, I’ve been switching between English and German a lot and the way you speak in one language can come across wrong in another!
> On 21 Sep 2016, at 01:58, Karl <raziel.im+swift-...@gmail.com> wrote: > > I’ve explained it so many times, do you really not understand it? > > Basically, protocols are explicit things. They are **not** things that you > just “discover” about types at all. We have protocols with no members > (“marker protocols”) which are basically used to tag certain types with no > other guarantees. That is the important difference between protocols in Swift > and in Objective-C. Even if you have implementations for every requirement, > if you never explicitly declared conformance you don’t conform. Period. > > However, the requirements themselves don’t need to be explicitly marked-up. > They just get automatically assigned by the compiler; maybe my property named > “count” doesn’t mean the same thing as Collection’s “count”? > > I’ve written a lot trying to explain this; please tell me what you don’t > understand. To summarise (again): > > - Members which satisfy a protocol requirement will have to be explicit about > which protocol they belong to > ——> They will no longer be automatically assigned as the witness for a > protocol requirement > > - Members which satisfy a protocol requirement will have the protocol name in > their symbolic names (e.g. MyType.someFunction() —> > MyType.AProtocol.someFunction()) > ——> this will allow the same type to conform to protocols with overlapping > names, free protocol designers from naming constraints, and reduce the need > for “helper structs” to represent alternate views of the same data. > ——> Extensions which declare conformance have to satisfy all of their > requirements explicitly - by implementing, forwarding to other functions, > stored properties or other objects. We can have a shorthand to make this easy. > > IMO, we definitely need to solve the overlapping naming problem. If we > explicitly assign protocol methods to protocols (as the thread was originally > about), this falls out of it. > > >> On 21 Sep 2016, at 01:25, Xiaodi Wu <xiaodi...@gmail.com >> <mailto:xiaodi...@gmail.com>> wrote: >> >> This was the conversation we were having in the other thread. Perhaps I'm >> still not understanding something, but I'm not convinced that this feature >> is an improvement. >> >> Currently, protocols represent a declaration that you have discovered that >> your type has certain semantics and guarantees a certain API, and you >> declare that fulfillment "by name only," just as you invoke members "by name >> only"; unintentional failure to fulfill the contract is a compile-time >> error. The status quo is precisely my idea of what protocols should be. What >> are the two ideas you have about them? >> On Tue, Sep 20, 2016 at 18:02 Karl <razie...@gmail.com >> <mailto:razie...@gmail.com>> wrote: >> I’m using String as an example of where this issue of conformance conflicts >> crops up in the standard library. Ideally, String (or any data type) should >> be able to conform to protocols whose requirements have conflicting names. >> Currently, in order to work around this limitation, you have to delegate the >> conformance to a supporting type. This is more complicated to write and >> maintain, and pollutes your internal API. String gives us an example of >> this, but it’s not the worst example. >> It basically implements what I’m talking about anyway, but manually via a >> supporting type instead of directly inside the data-type (String). As an ABI >> consideration, we should scope all protocol members to their protocols. This >> would resolve all naming conflicts. >> >> I can’t understand how anybody would argue for the status quo - we’re >> currently in-between two ideas of what protocols should be - they are >> explicit but their conformances are resolved by name only and can overlap >> without warning. >> >>> On 21 Sep 2016, at 00:48, Xiaodi Wu <xiaodi...@gmail.com >>> <mailto:xiaodi...@gmail.com>> wrote: >>> >>> Sorry, I'm still not sure I understand what you're getting at about this. >>> How would String conforming to Collection multiple times simplify or >>> improve the implementation of String? Or are you arguing it would be better >>> for users of String? If so, how? >>> >>> On Tue, Sep 20, 2016 at 17:26 Karl <razie...@gmail.com >>> <mailto:razie...@gmail.com>> wrote: >>> I’m not saying vital support is missing, just that it is more awkward. >>> String doesn’t conform to collection, String.UTF8View does; so if you >>> change some implementation detail (in StringCore, because that’s where they >>> live for String), you get the error popping up somewhere other than the >>> place you just changed. That’s what I mean when I say “language support”. >>> If you do what StringCore does, and you’re changing stuff which is >>> ultimately going to be used to conform to, say, Collection, you have to >>> build distance between the implementation and the (only) conformance it is >>> used for, and it’s less optimal. >>> >>> Let’s say I have an object MyComplexDataType, it implements >>> “InternalStructureView” and “Collection”: >>> >>> ``` >>> protocol InternalStructureView { >>> associatedtype Index >>> var count : Int { get } // whatever, just some stuff that will cause a >>> name conflict >>> func doInternalMagic(at: Index) >>> } >>> >>> struct MyComplexDataType { >>> var __collection_count : Int { >>> // This is quite a complex operation which we’d rather leave inside >>> the type, otherwise we’d need to expose a bunch of implementation details >>> internally >>> } >>> var __internal_count : Int { >>> // Again, best left here >>> } >>> >>> struct CollectionView : Collection { >>> init(parent: MyComplexDataType) { … } >>> var count { return parent.__collection_count } >>> // ...etc >>> } >>> >>> struct InternalStructure : InternalStructureView { >>> init(parent: MyComplexDataType) { … } >>> var count { return parent.__internal_count } >>> // ...etc >>> } >>> >>> var collection : CollectionView { return CollectionView(self) } >>> var internalStructure : InternalStructure { return >>> InternalStructure(self) } >>> } >>> ``` >>> >>> This is basically what String does (except that it wants to conform to >>> Collection multiple times with different indexes and results). It’s a lot >>> of work to maintain, especially if you have evolving protocols that are >>> conformed to in several places. >>> We should have a better solution. We should be able to define: >>> >>> “protocol UTF8Collection : Collection {} >>> protocol UTF16Collection : Collection {}” >>> >>> and have String conform to both of them at the same time. At the same time, >>> since we’re now being explicit about which protocol requirement is >>> satisfied where - we should also be able to delegate our conformance, >>> telling the compiler to dispatch any unimplemented methods to another >>> object. For example, lets say you want to wrap a Collection and observe >>> mutations to it; you might override replaceSubrange(), but every other >>> method (such as count, index(after:)… all the rest) is just a forwarding >>> function. >>> >>> Interestingly, we could do this safely (from a code legibility perspective) >>> if we say that every scope which adds a conformance must completely satisfy >>> its requirements, which we would more reasonably be able to require if we >>> made this change (so your internal functions can still be wherever you >>> like, but they won’t automatically satisfy a protocol requirement if they >>> happen to have the same name). Then we could reasonably say that if you add >>> an extension which adds a conformance to, say, Collection, you have to tell >>> us where to find every one of its requirements. That’s where we could put >>> the forwarding syntax for retroactive modelling. Stored properties can’t be >>> defined in extensions, so if you want to back an implementation with one, >>> you’ll need to make its conformance explicit in the main body (or we loosen >>> that to at least extensions in the same file). >>> >>> ``` >>> // generates thunks to members of this extension in the base type; >>> // so MyComplexDataType.count —> MyComplexDataType.InternalStructure.count, >>> // to account for conformance being added in later version. Can also be >>> used for renamed protocols, and be tagged on individual members. >>> >>> @makeAvailable(as: _) >>> extension MyComplexDataType : InternalStructure { >>> >>> typealias Index = InternalIndexType >>> var count : Int { >>> // We have access to all of the private members because we’re >>> inside MyComplexDataType >>> // No need to pollute internal API with conformance implementation >>> details. >>> // Also, we get errors about non-conformance where we want them — >>> where the implementation is. >>> } >>> func doInternalMagic(at: Index) { >>> ... >>> } >>> } >>> ``` >>> >>>> On 20 Sep 2016, at 23:46, Xiaodi Wu <xiaodi...@gmail.com >>>> <mailto:xiaodi...@gmail.com>> wrote: >>>> >>>> I'm not sure I understand. What compiler or language support is missing >>>> for StringCore? >>>> On Tue, Sep 20, 2016 at 16:42 Karl via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>> On 20 Sep 2016, at 23:28, Karl <raziel.im+swift-...@gmail.com >>>>> <mailto:raziel.im+swift-...@gmail.com>> wrote: >>>>> >>>>> >>>>>> On 20 Sep 2016, at 18:43, Nevin Brackett-Rozinsky via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> I have been following this discussion (as well as similar threads >>>>>> earlier this year) and listening to the ideas put forth by all sides. >>>>>> >>>>>> It seems to me that the fundamental difference between classes and >>>>>> protocols is that classes inherit implementation whereas protocol >>>>>> conformance is a promise about interface. >>>>>> >>>>>> When a class or struct or enum declares itself as conforming to a >>>>>> protocol, that means it has all the members specified in the protocol. >>>>>> The protocol conformance simply codifies a fact about the type itself: >>>>>> namely that all those members are present. >>>>>> >>>>>> In this model, any keyword such as `implements` on each conforming >>>>>> member would introduce substantial boilerplate for negligible gain. The >>>>>> purpose of a protocol is to communicate that certain members are >>>>>> available, not to make declaring those members more onerous. >>>>>> >>>>>> However, default implementations for protocols blur the line. Now there >>>>>> is actual implementation being inherited. A conforming type may choose >>>>>> to roll its own version of a method, or to utilize the default provided >>>>>> by the protocol. This is closer to the situation with subclassing. >>>>>> >>>>>> Moreover, a protocol which conforms to another protocol may itself >>>>>> define (or redefine!) default implementations for members of that other >>>>>> protocol. This can create “inheritance chains” of protocol default >>>>>> implementations. I think there is value in being able to refer to (and >>>>>> call) the inherited default implementation through some sort of `super` >>>>>> functionality. >>>>>> >>>>>> On the other hand, the existence of a default implementation in a >>>>>> protocol is in large part merely a convenience: a courtesy so that each >>>>>> conforming type need not rewrite the same boilerplate code. >>>>>> >>>>>> A type which conforms to a protocol may accept the default or it may >>>>>> provide its own implementation, but it is not “overriding” anything. The >>>>>> default implementation was offered as a convenience, to be taken or left >>>>>> as needed. Thus I do not think any keyword (neither `override` nor >>>>>> `implements`) should be required in that case either. >>>>>> >>>>>> The frequently-raised point regarding near-miss member names deserves >>>>>> some attention. Several people have expressed a desire for the compiler >>>>>> to assist them in determining whether a given member does or does not >>>>>> meet a protocol requirement. Specifically, when a type conforms to a >>>>>> protocol with a default implementation, and the type defines a member >>>>>> with a similar signature, it is not obvious at glance if that member >>>>>> matches the protocol. >>>>>> >>>>>> I think this is a job for linters and IDEs. For example, syntax >>>>>> highlighting could distinguish members which satisfy a protocol >>>>>> requirement, thereby providing immediate visual confirmation of success. >>>>>> >>>>>> Having followed the lengthy discussion and weighed the numerous ideas >>>>>> put forth, I come down firmly on the side of no keyword for protocol >>>>>> conformance. >>>>>> >>>>>> A protocol describes an interface and provides a set of customization >>>>>> points. It may also, as a convenience, offer default implementations. >>>>>> The protocol simply describes the capabilities of its conforming types, >>>>>> and any default implementations are there to make things easier for them. >>>>>> >>>>>> Conforming types should not be afflicted with extraneous keywords: that >>>>>> would run contrary to the purpose of having protocols in the first place. >>>>>> >>>>>> Nevin >>>>>> >>>>>> >>>>>> On Tue, Sep 20, 2016 at 11:16 AM, Xiaodi Wu via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> As I mentioned above, I agree that better diagnostics for near-misses >>>>>> are necessary, but they are possible without new syntax. There is no win >>>>>> in avoiding unintentional behavior because, without a default >>>>>> implementation, these issues are caught at compile time already. >>>>>> >>>>>> On Tue, Sep 20, 2016 at 10:14 Vladimir.S via swift-evolution >>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>>>>> >>>>>> > extension P { >>>>>> > implement func foo() -> [String : String] { return [:] } >>>>>> > } >>>>>> >>>>>> Yes, it seems like we need `implement` (or `override` as another >>>>>> suggestion) in protocol extension also just for the same reasons - be >>>>>> clear >>>>>> about our intention regarding implementing the requirement, to show that >>>>>> this func *depends* on the previous definition of P protocol and to avoid >>>>>> possible mistakes related to protocol conformance. >>>>>> >>>>>> On 20.09.2016 17:38, Charles Srstka wrote: >>>>>> >> On Sep 20, 2016, at 8:17 AM, Vladimir.S via swift-evolution >>>>>> >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>> >> <mailto:swift-evolution@swift.org >>>>>> >> <mailto:swift-evolution@swift.org>>> wrote: >>>>>> >> >>>>>> >> On 20.09.2016 3:03, Xiaodi Wu via swift-evolution wrote: >>>>>> >>> I definitely think Vladimir's suggestion is a great starting point, >>>>>> >>> IMO. >>>>>> >>> >>>>>> >>> However, I think it could be improved in one key respect where >>>>>> >>> previous >>>>>> >>> proposals using `override` are superior. Namely, the proposed >>>>>> >>> `implement` >>>>>> >>> keyword adds no additional safety when a type implements a protocol >>>>>> >>> requirement that doesn't have a default implementation. This is >>>>>> >>> because, if >>>>>> >> >>>>>> >> Yes, *at the moment of writing* the type's code there could be no >>>>>> >> default >>>>>> >> implementation for protocol requirement. But, *at the moment of >>>>>> >> compilation* such default implementation could appear. >>>>>> >> >>>>>> >> Let's discuss such scenario in case we'll take your suggestion: >>>>>> >> >>>>>> >> You got SomeClass.swift file, 3rd party file you don't want to change >>>>>> >> or >>>>>> >> changes are not allowed. Content: >>>>>> >> >>>>>> >> public protocol SomeProtocol { >>>>>> >> func foo() >>>>>> >> } >>>>>> >> >>>>>> >> public class SomeClass : SomeProtocol { >>>>>> >> func foo() {...} // no default implementation *at the moment of >>>>>> >> writing*, >>>>>> >> no need in `overload` >>>>>> >> } >>>>>> >> >>>>>> >> Now, you adds SomeClass.swift file to your project and in some *other* >>>>>> >> file you write: >>>>>> >> >>>>>> >> extension SomeProtocol { >>>>>> >> func foo() {...} >>>>>> >> } >>>>>> >> >>>>>> >> As you see, you don't control the SomeClass.swift but you suggest in >>>>>> >> this >>>>>> >> case SomeClass.foo() should be defined with `override`. >>>>>> >> >>>>>> >> With 'implement' SomeClass.foo() will be marked initially and will >>>>>> >> save >>>>>> >> us if protocol's requirement PLUS default implementation changed. >>>>>> > >>>>>> > Requiring the ‘implement’ keyword can help us even if no default >>>>>> > implementation is involved. Consider: >>>>>> > >>>>>> > protocol P { >>>>>> > func foo() -> [String : Any] >>>>>> > } >>>>>> > >>>>>> > struct S : P { >>>>>> > func foo() -> [String : String] { return [:] } >>>>>> > } >>>>>> > >>>>>> > We will get an error here that S does not conform to P. However, this >>>>>> > is >>>>>> > not the correct error, since S in fact *tries* to conform to P, but it >>>>>> > has >>>>>> > a mistake in a method signature. This misleads us as to the true >>>>>> > nature of >>>>>> > the problem, and if S has enough members in it that we fail to spot the >>>>>> > existing foo(), we might solve the problem by reimplementing foo(), and >>>>>> > leaving the original foo() as dangling dead code. Having an ‘implement’ >>>>>> > keyword on the existing foo() function would change the compiler error >>>>>> > to >>>>>> > let us know that we have an existing foo() that is incorrectly >>>>>> > declared. >>>>>> > >>>>>> > In addition, ‘implement’ can help us when the declaration in question >>>>>> > *is* >>>>>> > the default implementation: >>>>>> > >>>>>> > protocol P { >>>>>> > func foo() -> [String : Any] >>>>>> > } >>>>>> > >>>>>> > extension P { >>>>>> > implement func foo() -> [String : String] { return [:] } >>>>>> > } >>>>>> > >>>>>> > Here we will get an error with the proposed ‘implement’ keyword, >>>>>> > because >>>>>> > foo() does not have a signature matching anything in the protocol, >>>>>> > whereas >>>>>> > without ‘implement’ we would happily and silently generate a useless >>>>>> > dangling function that would never be used, and then pass the buck to >>>>>> > the >>>>>> > concrete type that implements P: >>>>>> > >>>>>> > protocol P { >>>>>> > func foo() -> [String : Any] >>>>>> > } >>>>>> > >>>>>> > extension P { >>>>>> > func foo() -> [String : String] { return [:] } // The error is here: >>>>>> > } >>>>>> > >>>>>> > struct S : P {} // But it gets reported here. >>>>>> > >>>>>> > Charles >>>>>> > >>>>>> _______________________________________________ >>>>>> 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> >>>>> >>>>> >>>>> I agree that a new keyword is unwanted. Conforming to protocols is quite >>>>> a common thing, so you want it to be easy to remember. >>>>> >>>>> I think the best way is to prefix the member name with the protocol, e.g: >>>>> >>>>> protocol MyProto { >>>>> var aVariable : Int >>>>> func aFunction() >>>>> } >>>>> class MyClass : MyProto { >>>>> var MyProto.aVariable : Int >>>>> func MyProto.aFunction() { … } >>>>> } >>>>> >>>>> This is consistent with how we refer to other members of types (e.g. >>>>> “extension MyClass.MyInternalClass”). It will be easy for autocompletion >>>>> to provide good suggestions, too. >>>>> As I see it, the only problem is what if `MyClass` wants its own function >>>>> called `aFunction()`? What if the same name satisfies 2 protocols, which >>>>> do you write? >>>>> >>>>> The way to solve all of the problems in a consistent way is to make the >>>>> function actually called “MyProto.aFunction”, and for it to be a separate >>>>> function from plain “aFunction()” or from “SomeotherProto.aFunction”. >>>>> >>>>> I believe it is crucial to protocols that we can do this. Maybe I have >>>>> some complex data structure and it has its own API, but I want people to >>>>> be able to view it as a Collection. By conforming to Collection, I >>>>> reserve lots of keywords and indexing operations which I now can’t use in >>>>> my own API. Maybe I’m just providing Collection as a convenience to work >>>>> with generic algorithms, but my own API has more efficient semantics for >>>>> some operations. We’re relegated to using less-obvious and legible names >>>>> in order to avoid conflicts. >>>>> >>>>> We have a way to work around this, which String uses - create a struct >>>>> which references your object and calls internal methods such as >>>>> “_collection_count” so you can have separate interfaces. This adds up to >>>>> quite a lot of boilerplate and maintenance overhead. >>>> >>>> Also to add here: you’re basically implementing what I’m proposing >>>> manually if you do this; only you don’t get language/compiler support. >>>> String basically does this - it shares StringCore with UTF8View and >>>> defines some internal functions to support it. >>>> >>>> The String views could then be made in to protocols on String, turning >>>> “UTF8View” in to “UTF8Representable”, and opening up algorithms which can >>>> work on generic sequences of UTF8 bytes. I think that’s pretty cool, and >>>> could open up better integration with other types which are (for example) >>>> UTF8Representable — for example a stream of UTF8 bytes (depending on how >>>> flexible implementation allows us to make the protocol). >>>> >>>>> >>>>> I don’t agree that Protocol conformances are kind-of incidental, as >>>>> others here have written. This isn’t like Objective-C where anything that >>>>> has the correctly-named methods conforms. Protocol conformances are >>>>> completely explicit, and in fact we have empty protocols (“marker >>>>> protocols”) for exactly that purpose. I think it is consistent that we >>>>> make every member of a conformance specify which protocol it belongs to, >>>>> and to have its name scoped to that protocol. >>>>> >>>>> Karl >>>>> >>>>> >>>>> CC-ing Dave A, to understand better if this fits with the vision of >>>>> protocols >>>>> >>>> _______________________________________________ >>>> 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