<div dir="ltr">On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div>In response to Jordan Rose's comment I suggest the following change:</div><div id="m_-5335225969984223547AppleMailSignature"><br></div><div id="m_-5335225969984223547AppleMailSignature"><p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Proposal: Split extension usage up into implementing methods and adding static functions</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Currently extension methods are confusing because they have different dispatch rules for the same syntax. EG:</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> protocol P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m()</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> extension P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m() { print("P.m") }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> struct S: P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m() { print("S.m") }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> val p: P = S() // Note typed as P</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> p.m() // Surprisingly prints P.m even though S implements its own m</span></p></div></div></blockquote><div><br></div><div>This is incorrect. This prints "S.m", not "P.m".</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div id="m_-5335225969984223547AppleMailSignature">
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> val s = S() // Note typed as S</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> s.m() // Prints S.m as expected </span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">This proposal cures the above problem by separating extension methods into two seperate use cases: implementations for methods and adding static functions. </span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">First implementing methods.</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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:</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">File P.swift</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> protocol/struct/class P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m()</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> extension P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m() { print("P.m") }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Same or other file</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> struct S: P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> override func m() { print("S.m") } // Note override required because m already has an implementation from the extension</span></p></div></div></blockquote><div><br></div><div>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.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div id="m_-5335225969984223547AppleMailSignature">
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> val p: P = S() // Note typed as P</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> p.m() // Now prints S.m as expected </span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations. </span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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. </span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">The second use case is adding static functions.</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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:</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> static final extension P { // Note extension marked static final</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> func m() { print("P.m") }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Which is called as any other static function would be called:</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> val s = S()</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> P.m(s) // Prints P.m as expected</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">The new static final extension is shorthand, particularly in the case of multiple functions, for:</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> extension P {</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> static final func m(_ this: P) { print("P.m") }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt"> }</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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.</span></p></div></div></blockquote><div><br></div><div>What is the use case for having this restriction? What is the problem you are trying to solve? </div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto"><div id="m_-5335225969984223547AppleMailSignature"><p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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.</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">In summary.</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">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.</span></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69);min-height:20.3px"><span style="font-family:'.SFUIText';font-size:17pt"></span><br></p>
<p style="margin:0px;line-height:normal;font-family:'.SF UI Text';color:rgb(69,69,69)"><span style="font-family:'.SFUIText';font-size:17pt">Note the distinction between an extension in the same file and in a separate file is consistent with the proposed use of private in <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md" target="_blank">https://github.com/apple/<wbr>swift-evolution/blob/master/<wbr>proposals/0169-improve-<wbr>interaction-between-private-<wbr>declarations-and-extensions.md</a><wbr>.</span></p><div id="m_-5335225969984223547AppleMailSignature"><br></div>Comments?</div><div id="m_-5335225969984223547AppleMailSignature"><br>-- Howard.</div><div><br>On 7 Apr 2017, at 4:49 am, Jordan Rose <<a href="mailto:jordan_rose@apple.com" target="_blank">jordan_rose@apple.com</a>> wrote:<br><br></div><blockquote type="cite"><div><div>[Proposal: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md" target="_blank">https://github.com/<wbr>apple/swift-evolution/blob/<wbr>master/proposals/0164-remove-<wbr>final-support-in-protocol-<wbr>extensions.md</a>]</div><br><div><blockquote type="cite"><div>On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br class="m_-5335225969984223547Apple-interchange-newline"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="background-color:rgba(255,255,255,0)">The review of SE-0164 "Remove final support in protocol extensions"</span></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><ul style="box-sizing:border-box;padding-left:2em;margin-top:0px;margin-bottom:16px"><li style="box-sizing:border-box"><font><span style="background-color:rgba(255,255,255,0)">What is your evaluation of the proposal?</span></font></li></ul></blockquote><div>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. </div><div><br></div><div>However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs. </div><div><br></div><div>I think a better solution would be one of the following alternatives:</div><div><br></div><div> 1. Must use final and final means it cannot be overridden; or</div><div> 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</div><div> 3. Must be marked dynamic and uses obj-c dispatching. </div><div><br></div><div>My preference would be option 2 but I think any of the three is superior to the present situation or the proposal. </div></div></div></blockquote><div><br></div><div>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.</div><div><br></div><div>(I <i>think</i> 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.)</div><div><br></div><div>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.</div><div><br></div><div>Jordan</div></div></div></blockquote></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div></div>