[swift-evolution] [Pitch] Requiring proactive overrides for default protocol implementations.
Erica Sadun
erica at ericasadun.com
Mon May 9 13:03:20 CDT 2016
The pitch was not warmly received. If you want to pick it up and run with it, go ahead.
https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a <https://gist.github.com/erica/fc66e6f6335750d737e5512797e8284a>
I have a running list of dead or deferred ideas here: https://gist.github.com/erica/9eae0d949297509ad86e
-- E
> On May 9, 2016, at 11:49 AM, Vladimir.S <svabox at gmail.com> wrote:
>
> Hi Erica, could you clarify, what is state of this proposal and your plans regarding it? I believe we certainly should make Swift more explicit regarding what methods in type are required by the conformed protocol and what methods are required(and which are 'optional') in protocol extensions.
>
> Right now there is a discussion regarding 'optional' keyword("Modify optional method semantics for swift"), and I remembered your proposal..
>
> Probably 'optional' keyword for non-required methods in extension instead of marking 'required' methods will looks better(as they are optional to the protocol itself), what do you think?
> I.e.
>
> 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..
>
> func blort() {} // Correct, was introduced in `A`
> optional func womble() {} // Correct, new(optional) method in extension
> optional func gar() {} // Incorrect: Compiler says: remove `optional`..
> }
>
> 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
>
> func womble() {} // ?? how this method should be 'marked' ??
> }
>
> (But personally I think one *overload* keyword will do the job in both cases - in extension and in type declaration)
>
> Regarding this "func womble()" questions.. I think we need *at least* compilation warning that *at the moment of compilation*, B.womble may(?) conflicts with extension of A.womble.
>
> Personaly I was not expecting to get the result of this code:
>
> protocol A {
> func a()
> }
>
> extension A {
> func b() { print("(b) in A") }
> }
>
> struct C : A {
> func a() {}
> func b() { print("(b) in C") }
> }
>
> var c : A = C()
> c.b() // (b) in A
>
>
>> On 28.04.2016 19:53, Erica Sadun via swift-evolution wrote:
>>> 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 <dgregor at apple.com
>>>> <mailto:dgregor at apple.com>> wrote:
>>>
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160509/6604e114/attachment.html>
More information about the swift-evolution
mailing list