On 11.04.2017 1:51, Xiaodi Wu via swift-evolution wrote:
> On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution
> <[email protected] <mailto:[email protected]>> wrote:
>
>     In response to Jordan Rose's comment I suggest the following change:
>
>     Proposal: Split extension usage up into implementing methods and adding
>     static functions
>
>
>     Currently extension methods are confusing because they have different
>     dispatch rules for the same syntax. EG:
>
>
>         protocol P {
>
>             func m()
>
>         }
>
>         extension P {
>
>             func m() { print("P.m") }
>
>         }
>
>         struct S: P {
>
>             func m() { print("S.m") }
>
>         }
>
>         val p: P = S() // Note typed as P
>
>         p.m() // Surprisingly prints P.m even though S implements its own m
>
>
> This is incorrect. This prints "S.m", not "P.m".

I believe the discussion was started is about protocol extensions methods that was not declared as protocol requirements:

protocol P {
        func foo()
}

struct S : P {
        func foo() {print("FOO from S")}
}

//----------------

extension P {
        func foo() {print("FOO default implementation from protocol")}
        
        // notice: bar was not declared as protocol requirement
        func bar() {print("BAR default implementation from protocol")}
}

extension S {
        func bar() {print("BAR from S")}
}

var  p : P = S()
p.foo() // FOO from S
p.bar() // BAR default implementation from protocol

var  s :S = S()
s.foo() // FOO from S
s.bar() // BAR from S

This is actually a big confusion point raised periodically in list and on stackoverflow etc. When you know how it works currently - you have no questions, but before this you can think that P.bar is same "thing" as P.foo. IMO Swift should help to clarify this situation with some kind of keyword/warning or actually make P.bar use the same rules as P.foo. After all, P.bar is a method in P protocol, S conforms to P protocol, so p.bar() logically should call S.bar() implementation.

At least IMO we need some marker for such protocol extension method that is not declared in protocol itself as requirement.

extension P {
        func foo() {...}
        
func bar() {...} // Warning: 'bar' is not protocol requirement, use 'notrequirement' keyword
}

var  p : P = S()
p.foo()
p.bar() // Warning: 'bar' is not-a-requirement method defined without 'notrequirement' keyword

So, to fix the warning:

extension P {
        func foo() {...}
        
        notrequirement func bar() {...}
}

('notrequirement' is not a proposal, just an example)

Personally I'd even require a "marker" on caller side, for example

p.bar() // Warning: 'bar' is not-a-requirement method, exact implementation of P.bar() will be called. Use explicit casting to silence the warning

(p as P).bar() // ok


>
>
>         val s = S() // Note typed as S
>
>         s.m() // Prints S.m as expected
>
>
>     This proposal cures the above problem by separating extension methods
>     into two seperate use cases: implementations for methods and adding
>     static functions.
>
>
>     First implementing methods.
>
>
>     If the extension is in the same file as the protocol/struct/class
>     declaration then it implements the methods and is dispatched using a
>     Vtable. EG:
>
>
>     File P.swift
>
>         protocol/struct/class P {
>
>             func m()
>
>         }
>
>         extension P {
>
>             func m() { print("P.m") }
>
>         }
>
>
>     Same or other file
>
>         struct S: P {
>
>             override func m() { print("S.m") } // Note override required
>     because m already has an implementation from the extension
>
>
> Requiring `override` breaks retroactive conformance of types to protocols.
> This idea has been brought up over half a dozen times. Each time it fails
> in not being able to accommodate retroactive conformance.

FWIW Actually, there were a number of suggestions where this point is solved with some additional keywords/syntax. But as I remember they also was not accepted.

IIRC something like this:

protocol P {
        func foo()
}

struct S : P {
        func foo() {} // no default implementation was known at the moment of
                      // *writing* this code
}

//another file

extension P {
        func foo() {} // this causes warning for S declaration "'override' is 
missed"
        func bar() {} // not-a-requrement
}

extension S {
        overriden foo() // silence the warning, notice 'overriden', no body
        
        override bar() {} // so S.bar should be called on S instance typed as P
}


Or this case:

struct S {
        func foo() {}
        func bar() {}
}

// another file

protocol P {
        func foo()
}

extension P {
        func foo() {}
        func bar() {} // not-a-requrement
}

// another file

extension S : P {
        overriden foo() // silence the warning
        overriden bar() // silence the warning
        
        // or even:
        //overriden P // instead of separate method names of P protocol
}


>
>
>         }
>
>         val p: P = S() // Note typed as P
>
>         p.m() // Now prints S.m as expected
>
>
>     Extensions in the same file as the declaration can have any access, can
>     be final, and can have where clauses and provide inheritable
>     implementations.
>
>
>     The implementation needed to achieve this is that a value instance
>     typed as a protocol is copied onto the heap, a pointer to its Vtable
>     added, and it is passed as a pointer. IE it becomes a class instance.
>     No change needed for a class instance typed as a protocol.
>
>
>     The second use case is adding static functions.
>
>
>     A new type of extension is proposed, a static final extension, which
>     can be either in or outside the file in which the protocol/struct/class
>     declaration is in. EG:
>
>
>         static final extension P { // Note extension marked static final
>
>             func m() { print("P.m") }
>
>         }
>
>
>     Which is called as any other static function would be called:
>
>
>         val s = S()
>
>         P.m(s) // Prints P.m as expected
>
>
>     The new static final extension is shorthand, particularly in the case
>     of multiple functions, for:
>
>
>         extension P {
>
>             static final func m(_ this: P) { print("P.m") }
>
>         }
>
>
>     If the static final extension is outside of the file in which the
>     protocol/struct/class declaration is in then the extension and the
>     methods can only have fileprivate and internal access.
>
>
> What is the use case for having this restriction? What is the problem you
> are trying to solve?
>
>
>
>     As at present protocol/struct/class can have both a static and instance
>     method of the same name, m in the case of the example, because the
>     usage syntax is distinct. As at present, static final extensions, both
>     the extension and the individual functions, can have where clauses.
>
>
>     In summary.
>
>
>     The proposal formalises the split use of extensions into their two
>     uses: implementing methods and adding static functions. Syntax is added
>     that clarifies both for declarations and usage which type of extension
>     is provided/in use.
>
>
>     Note the distinction between an extension in the same file and in a
>     separate file is consistent with the proposed use of private in
> https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md > <https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md>.
>
>
>     Comments?
>
>     -- Howard.
>
>     On 7 Apr 2017, at 4:49 am, Jordan Rose <[email protected]
>     <mailto:[email protected]>> wrote:
>
>> [Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md >> <https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md>]
>>
>>>     On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution
>>>     <[email protected] <mailto:[email protected]>> wrote:
>>>
>>>     The review of SE-0164 "Remove final support in protocol extensions"
>>>
>>>>       * What is your evaluation of the proposal?
>>>     The present situation isn't great. People get confused about which
>>>     method will called with protocol extensions. Seems like every week
>>>     there is a variation on this confusion on Swift Users mailing list.
>>>     Therefore something needs to be done.
>>>
>>>     However I am not keen on this proposal since it makes behaviour
>>>     inconsistent between methods in protocol extensions, classes, and
>>>     structs.
>>>
>>>     I think a better solution would be one of the following alternatives:
>>>
>>>       1. Must use final and final means it cannot be overridden; or
>>>       2. If not final dispatches using a table like a class and if
>>>     marked final cannot be overridden and if marked dynamic uses obj-c
>>>     dispatching; or
>>>       3. Must be marked dynamic and uses obj-c dispatching.
>>>
>>>     My preference would be option 2 but I think any of the three is
>>>     superior to the present situation or the proposal.
>>
>>     People have suggested all of these before, but none of them are
>>     obviously correct. It's true that we have a difference between
>>     extension members that satisfy requirements and those that don't, and
>>     that that confuses people. However, an extension-only member of one
>>     protocol can be used to satisfy the requirements of another protocol
>>     today, which is a tool for code reuse.
>>
>>     (I /think/ we managed to convince everyone that it's just a bug that
>>     a protocol extension method that satisfies a requirement cannot be
>>     overridden in a subclass, so at least that isn't an issue on top of
>>     the rest of this.)
>>
>>     Oh, and we can't retroactively add members of a protocol extension to
>>     existing adopters, which is why protocol extension members cannot be
>>     @objc. There are limited circumstances where that would be safe, but
>>     that would be a separate proposal.
>>
>>     Jordan
>
>     _______________________________________________
>     swift-evolution mailing list
>     [email protected] <mailto:[email protected]>
>     https://lists.swift.org/mailman/listinfo/swift-evolution
>     <https://lists.swift.org/mailman/listinfo/swift-evolution>
>
>
>
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
>

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution
              • ... Xiaodi Wu via swift-evolution
              • ... Thorsten Seitz via swift-evolution
              • ... Howard Lovatt via swift-evolution
              • ... Xiaodi Wu via swift-evolution
              • ... Howard Lovatt via swift-evolution
              • ... Brent Royal-Gordon via swift-evolution
              • ... Howard Lovatt via swift-evolution
              • ... Xiaodi Wu via swift-evolution
              • ... Xiaodi Wu via swift-evolution
              • ... David Waite via swift-evolution
          • Re: [swif... Vladimir.S via swift-evolution
          • Re: [swif... Adrian Zubarev via swift-evolution
            • Re: ... Jakub Suder via swift-evolution
  • Re: [swift-evolution] [Rev... Víctor Pimentel Rodríguez via swift-evolution
  • Re: [swift-evolution] [Rev... Jordan Rose via swift-evolution

Reply via email to