[swift-evolution] [Pitch] Requiring special keyword to mark protocol implementation methods

Vladimir.S svabox at gmail.com
Wed May 18 02:28:09 CDT 2016


I'd like to discuss declaration of protocol implementation in types.

Ideas of this pitch is partially based on Erica Sadun's thread:
[Pitch] Requiring proactive overrides for default protocol implementations.
http://thread.gmane.org/gmane.comp.lang.swift.evolution/15496
And on this draft of proposal:
https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a

I had read "Winding down the Swift 3 release" message, but feel like this 
proposal makes "improvements to the consistency and feel of the language"
and supports the "goal of making Swift 4 as source compatible with Swift 3 
as we can reasonably accomplish". Or this can be discussed as 'after Swift 
3.0' feature.


The main idea of my proposal

Swift should help us to be explicit about what methods were defined in type 
specifically to fulfill a protocol requirements. And so we will have more 
understandable and self-documented code that is explicit regarding the fact 
of protocol implementation(for reader of our code and for compiler).

Additionally this can help to reduce potential 
problems/error/misunderstanding in case some protocol requirement(method) 
were *removed* - in this case (currently) our type will still have a method 
as implementation but no such requirement in conformed protocol yet.


Details

I propose to introduce *implement* keyword to 'mark' the methods(in type) 
that were defined in order to conform to protocol.
Also, *reimplement* keyword is proposed to reflect the fact that method is 
declared in *both* : in the type and in protocol extension (only in 
extension, no definition of the method in protocol itself)


Rules

* Each method in type defined to fulfill the protocol conformance should be 
marked with `implement` keyword:
implement func protocolRequirement() {..}

* Compiler produces *warning* if the same function is declared in type and 
in protocol extension in case class conformed to that protocol. To 'fix' 
the warning, `reimplement` should be used.
reimplement func f() {..} // we aware that the same method in protocol 
extension

* don't need to mark methods for type if conformance to protocol defined by 
separate type extension

* `reimplement` can be placed in defining type or as special declaration in 
type extension anywhere in source code:
extension Type {
   reimplement methodDeclaredInBothTypeAndInProtocolExtension(..); // no 
body here. probably `;` is required - to be discussed
}
(I don't really like this requirement, but right now don't know if we can 
do the same by using any other method )


Current behavior of protocol extension

When method is declared in protocol extension(*only. no in protocol 
itself*) and in type that is conformed to the protocol, there is a 
possibility for confusion about which method will be called. Example:

protocol A { func a() }
extension A { func b() {print("Hello from b() in extension A")} }

class B : A {
     func a() {}
     func b() {print("Hello from b() in B")}
}

let instance1 = B()
instance1.b() // Hello from b() in B

let instance2 : A = B()
instance2.b() // Hello from b() in extension A

func f<T:A>(t: T) { t.b() }
f(instance1) // Hello from b() in extension A

In this proposal I suggest compiler will generate a *warning* in this 
case(not error). This warning can be fixed with `reimplement` keyword for 
b() in class B.


`Override` keyword alternative

I think that `override` keyword can not be used instead of new `implement`, 
as the first clearly means now that method overrides one of the methods 
from base class. Such method already has implementation in base class(in 
compare with protocol requirement) and 'belongs' to `class` not to 
`protocol`. So, in case of `override` keyword for protocol implementation 
we'll have questions if super.method() should be called inside.
Also, usually declaration of `override` method is not required, in opposite 
to implementation of protocol requirement.


Questions to discuss

* Should `reimplement` be used only when exactly the same method 
declared(arguments+return type the same) or even if just method name is the 
same(in extension there is f(v: Float) in type f(v:Double)) ? I'd prefer 
the second, but I don't feel this will be supported by community.


Code Examples:

A) ------------------------------------------------

1. We have some protocol:

protocol A {
     func a()
     func b()
     func c()
}

2. Then extension is defined for it:

extension A {
     implement func a() {} // Correct, `a` was declared in `A`

     //func b() {}
     // Incorrect: `b` was declared in `A`, `implement` is required
     // Compiler says: add `implement` keyword or remove implementation

     func d() {} // Correct, new method in extension

     //implement func e() {}
     // Incorrect: `e` was not declared in `A`, `implement` should be removed
     // Compiler says: remove `implement` keyword or remove implementation
}

3. *We* are writting the type S that conforms to A protocol:

struct S: A {
     implement func a() {} // Correct. We implemented A's requirement

     //func b() {}
     // Incorrect: add `implement` keyword or rename the method

     implement func b() {} // Correct
     implement func c() {} // Correct

     //implement func f() {}
     // Incorrect: No `f` declaration in protocol. Compiler: rename method 
or drop `implement` keyword

     //func d() {}
     // Incorrect: Possible accidental name match.
     // Compiler: rename method or add `reimplement` keyword

     reimplement func d() {} // Correct. We are explicit about the fact 
that our type has the same function as in extension of conformed protocol
}


B) ------------------------------------------------

1. Having declared protocol:

protocol A {
     func a()
     func b()
     func c()
}

2. Have defined extension

extension A {
     implement func a() {} // Correct, `a` was declared in `A`

     //func b() {}
     // Incorrect: `b` was declared in `A`, `implement` is required
     // Compiler says: add `implement` keyword or remove implementation

     func d() {} // Correct, new method in extension

     //implement func e() {}
     // Incorrect: `e` was not declared in `A`, `implement` should be removed
     // Compiler says: remove `implement` keyword or remove implementation
}

3. Have a type that we can't/don't want to change

struct S { // at this moment S don't care about any protocol
     func a() {}
     func b() {}
     func c() {}
     func d() {}
}

4. *We* need (for some task) to conform the S type to protocol A in our own 
code. So we do:

extension S : A {
     // no need of any special steps for a(), b(), c()
     // as we just said "we sure S conforms to A"

     // but we need to care about special case - d() method, which is 
defined in S type and in extension of A protocol
     reimplement d();  // probably `;` is required here
}


C) ------------------------------------------------

1. Have protocol

protocol A {
     func a()
     func b()
     func c()
}

2. Have a Type

struct S { // at this moment S don't care about any protocol
     func a() {}
     func b() {}
     func c() {}
     func d() {}
}

3. Conforms to protocol

extension S : A {
     // no need of any special steps for a(), b(), c()
     // as we just said "we sure S conforms to A"
     // here(at the moment of *writing*) S don't know about any extension of A
}

4. Now protocol extension is defined:

extension A {
     implement func a() {} // Correct, `a` was declared in `A`

     //func b() {}
     // Incorrect: `b` was declared in `A`, `implement` is required
     // Compiler says: add `implement` keyword or remove implementation

     func d() {} // Correct, new method in extension
}

5. But now we have a problem : same d() method in S and in A extension
Compiler will raise a warning : Possible accidental name match of d() in S, 
also defined in extension of A. Change method name or add `reimplement` keyword

In case we can't or don't want to change the method name(in type or in 
protocol) - the only solution is to say "it's OK" to the compiler anywhere 
in our code:

extension S {
     reimplement d();  // notify the compiler that we are aware of the same 
names
}

Thank you for your opinions on this.


More information about the swift-evolution mailing list