Draft. Criticism and suggestions both welcome. -- E

Requiring Proactive Overrides for Default Protocol Implementations

Proposal: tbd
Author(s): Erica Sadun <http://github.com/erica>
Status: tbd
Review manager: tbd
 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#introduction>Introduction

This proposal enhances protocol implementation safety. It incorporates two 
keywords that cooperate with compiler checks to limit "near miss" 
implementation errors and accidental member overrides.

This proposal was discussed on the Swift Evolution list in the [Pitch] 
Requiring proactive overrides for default protocol implementations. 
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/15496> thread

 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#motivation>Motivation

The proposal introduces a mandatory required keyword that marks members as 
fulfiling protocol requirements. This expansion reduces the risk of near-miss 
implementations (for example, adding thud(x: Double) when thud(x: Float)is 
required), provides in-line documentation of why the member has been included, 
thereby enhancing the code-level documentation at the implementation point, and 
supports compile-time checks for protocol conformance.

This proposal extends the override keyword to protocol conformance. The Swift 
Programming Language describes the way subclass methods must override 
implementations established in superclasses. Methods on a subclass that 
override the superclass’s implementation are marked with *override*—overriding 
a method by accident, without override, is detected by the compiler as an 
error. The compiler also detects methods with override that don’t actually 
override any method in the superclass.

Adding an override requirement expands this cautious approach to protocols. 
Developers must override implementations inherited from protocol extensions 
with the override keyword. And the compiler will flag uses of override where 
member implementations do not, in fact, override an existing implementation. 
The keyword prevents accidental overrides, where a sensible member name 
conflicts with signatures established in the protocol conformance and forces 
users to proactively select a version in favor of existing protocol extensions.

 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#detail-design>Detail
 Design

The override keyword is extended to protocol inheritance, and when used prefers 
the overridden behavior to the default behavior. 
Swift will prefer an overridden implementation in preference in reverse 
hierarchical order: type extensions take precedence over type declarations over 
protocol extensions over protocol declarations (assuming protocol declarations 
eventually adopt default implementations).
The required keyword marks a member as satisfying a protocol requirement, 
whether in protocol extensions, type declarations, or type extensions.
 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#required-protocol-members>Required
 Protocol Members

Protocol requirements are marked with required for compile-time checks of 
intentional conformance.

protocol A { 
    func foo() 
    func bar()
    func blort()
    func gar()
}

extension A {
    required func blort() {} // Correct, required by `A`
    func womble() {} // Correct, new method in extension
    func gar() {} // Incorrect: Compiler says: add `required` keyword or remove 
implementation
}

struct B: A {
    required func foo() {} // Correct
    required func far() {} // Near miss. Compiler: rename method or drop 
required keyword
    func bar() {} // Possible accidental name match. Compiler: rename method or 
add required keyword
}
 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#member-overrides>Member
 Overrides

Overrides are marked with override to ensure intent.

protocol A { 
    func foo() 
    func bar()
    func blort()
    func gar()
}

extension A {
    required func foo() {} // correct
    func womble() {} // correct
}

struct B: A {
    required func bar() {} // correct
    required func foo() {} // incorrect: Compiler says: add `override` keyword 
or remove implementation
     func womble() {} // incorrect: Compiler says add `override` keyword or 
remove implementation. `required` is not needed as `womble` is not a required 
protocol member.
}
 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#handling-changes>Handling
 Changes

Default implementations can be added or removed at any time, as can type 
conformance implementations:

**Original**    **Change**      **Outcome**
Some member implemented in type Protocol adds that member       Must add 
`required` to type implementation or rename member to avoid conflict
Some member implemented in type, marked as `required`   Protocol removes that 
member or it never existed        Must remove `required` from type 
implementation
Some member implemented in type, marked as `override`   Protocol extension 
removes that member or it never existed      Must remove `override` from type 
implementation
Some member implemented in typed, member not mentioned in protocol      
Extension adds default version of member        Type implementation must add 
`override` keyword
`required` member implemented in type   Default member added    Must add 
`override` or remove type implementation
`override required` member implemented in type  Remove default member   Must 
remove `override` in type implementation
`override required` member implemented in type  Remove type member 
implementation       Default implementation now used
Type member uses `required` keyword     Protocol removes requirement or never 
had it    Type implementation must remove `required` keyword
Protocol declares required member       Extension implements default 
implementation     Extension must add `required` keyword, differentiating 
default implementations from added behavior
Swift adds default implementations to protocols as well as extensions   
Protocol adds default implementation    Type implementation must use both 
`required` and `override` keywords. Protocol extension must use `override` 
keyword. Order of preference goes: overriden member, overriden extension, 
protocol default implementation
 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#multiple-conformance-conflict>Multiple
 Conformance Conflict

Consider the following situation. For the sake of future-proofing, this example 
includes default protocol implementations although they do not yet exist in 
Swift.

protocol A { func foo() {...default...} }
protocol B { func foo() {...default...} }
extension A { override required func foo() {...A extension...} }
Type CType: A, B {}
In this example, the compiler emits a warning that "CType cannot unambiguously 
differentiate which version of foo to use for CType instances". If the CType 
type were to be removed or either of its conformances erased, there would be no 
compiler issues.

To fix this scenario, CType must implement a version of foo that resolves the 
conflict:

Type CType: A, B { override required func foo() { 
    // either
    A.foo(self)() // uses the A extension default implementation
    // or
    B.foo(self)() // uses the B protocol default implementation
    // or both, one after the other, etc.
}
In this rewrite, foo is unambiguously referenced for CType instance members.

 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#impact-on-existing-code>Impact
 on Existing Code

These changes introduce mandates that do not exist in today's Swift code and 
will require migration. The migrator (and compiler) must detect both scenarios: 
that a member satisfies a protocol requirement and needs the required keyword, 
and that a member overrides a default implementation (in current Swift, only in 
extensions) and needs the overridekeyword.

In the degenerate case that protocol extensions provide two distinct default 
implementations of the same member (whether required or not), the override 
version should always be preferred. When multiple override versions exist, the 
compiler should emit a warning about ambiguous resolution.

Using type currying, e.g. A.foo(self) should always resolve using the rules 
enumerated earlier in this proposal, moving from type extensions to types to 
protocol extension to protocols.

 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#alternatives-considered>Alternatives
 Considered

Not at this time.

 
<https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a#acknowledgements-and-thanks>Acknowledgements
 and Thanks

Thanks, Doug Gregor, Jordan Rose, and Joe Groff




> On Apr 27, 2016, at 6:07 PM, Douglas Gregor <[email protected]> wrote:

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to