> 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 <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
> 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.

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

Reply via email to