Hi, Erica. Sorry for not participating in the first round here. I’m…not so 
happy with this direction, for a number of reasons. (I apologize for the 
laundry list, but they’re not really related complaints.)

- ‘required’ already means something today: it means “this initializer must be 
present on all subclasses”. The reason it only applies to initializers is 
because all other members are always present on all subclasses.

(Counter-argument: using ‘required’ on an initializer could be seen as making 
an implicit protocol, just for that class hierarchy.)

- ‘override’ likewise already has a meaning; allowing ‘override’ to be 
satisfied by a protocol requirement means that it might miss an override 
intended for a superclass—or inadvertently become one when an SDK is updated.

(Counter-argument: that last can happen to protocols already.)

- This doesn’t cover cases where methods in one protocol extension satisfy 
requirements in another.

- This doesn’t cover retroactive modeling.

- I’m not sure what it means to "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)”. Protocol 
conformance is decided at compile time; there won’t ever be any members in type 
extensions that take precedent over a type declaration without causing a 
conflict. (That is, currently you are not allowed to define such a member.)

- A member in the type does not “override" a member in a protocol extension 
today, because such a call is not dynamically dispatched. Making protocol 
extension members dynamically dispatched is challenging at the least and would 
require an implementation plan in the proposal.

- Thank you for writing up all of the source compatibility cases! I think 
there’s no issue with binary compatibility, since IIUC the proposal doesn’t 
change how anything is implemented, and we think we know how to handle binary 
compatibility there. But I’d like to think about it a little more.

- The “A.foo(self)()” syntax is clever, but it doesn’t work correctly for 
mutating methods (because you can’t curry an inout). On the other hand, JoeG 
already brought up the idea of making ‘self’ the first argument of the implicit 
static member. It still doesn’t solve the problem of picking a protocol 
extension, but that’s not new. (This isn’t a complaint, I guess, just a note.)


I’m not sure I have a meaningful summary or conclusion, but I’d be hesitant to 
do all of this without these concerns being addressed.

Jordan



> On Apr 28, 2016, at 09:53, Erica Sadun <[email protected]> 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: 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] 
>> <mailto:[email protected]>> wrote:
> 

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

Reply via email to