[swift-evolution] [Pitch] Requiring proactive overrides for default protocol implementations.

Vladimir.S svabox at gmail.com
Tue May 10 13:22:19 CDT 2016


I'm thinking about this.. but first, could someone *please* 
describe(clarify) the behavior of the next code and if that behavior is 
expected and if we really need exactly this behavior and if we can(I mean 
if it is not too hard) change this.. :

protocol A {
}

extension A {
     func a() { print("(a) in extension of A") }

     static func b() {print("(static b) in extension of A")}
}

struct S : A {
     func a() { print("(a) in S") }

     static func b() {print("(static b) in S")}
}

var s : A = S()

print("s.dynamicType = ", s.dynamicType)
s.a()
s.dynamicType.b()

Result is :

s.dynamicType =  S
(a) in extension of A
(static b) in extension of A

IMO This is definitely not the "less surprising" behavior.
Thank you.

On 09.05.2016 21:03, Erica Sadun wrote:
> 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
>
> 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
>> <mailto: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>
>>>>> <mailto:dgregor at apple.com>> wrote:
>>>>
>>>>
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>


More information about the swift-evolution mailing list