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> 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> 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> 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> 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> wrote:
>>
>>> On 20 Sep 2016, at 23:28, Karl <raziel.im+swift-...@gmail.com> wrote:
>>>
>>>
>>> On 20 Sep 2016, at 18:43, Nevin Brackett-Rozinsky via swift-evolution <
>>> 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> 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> 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>>
>>>>> 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
>>>>> 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 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
>>> 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