[swift-evolution] Mark protocol methods with their protocol

Vladimir.S svabox at gmail.com
Tue Sep 20 17:08:41 CDT 2016


On 21.09.2016 0:28, Karl via swift-evolution wrote:
>

I don't understand. Do you feel that this:

class MyClass : MyProto {
     var MyProto.aVariable : Int
     func MyProto.aFunction() { … }
}

better than this:

class MyClass : MyProto {
     implement var aVariable : Int
     implement func aFunction() { … }
}

?

>> On 20 Sep 2016, at 18:43, Nevin Brackett-Rozinsky via swift-evolution
>> <swift-evolution at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at swift.org>
>>         <mailto:swift-evolution at swift.org
>>         <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at swift.org>
>>         https://lists.swift.org/mailman/listinfo/swift-evolution
>>         <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>
>>
>>     _______________________________________________
>>     swift-evolution mailing list
>>     swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>     https://lists.swift.org/mailman/listinfo/swift-evolution
>>     <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at 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.
>
> 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>


More information about the swift-evolution mailing list