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: tbd2
<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 |override|keyword.
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]
<mailto:[email protected]>> wrote:
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution