<div dir="ltr"><span style="font-size:12.8px">Here is a proposal I put forwards last month that addresses the issue (and others), the original versions did not allow </span><span style="font-size:12.8px">retroactive modeling but following feedback it was incorporated</span><span style="font-size:12.8px">.</span><div><br></div><div>PS Bitten by this problem two days ago, copied some example Swift 2 code into a Swift 3 project and an obj-c method had changed its name. Took ages to find the culprit! The error was a null pointer exception it a completely different bit of code! Very frustrating!<br clear="all" style="font-size:12.8px"><div style="font-size:12.8px"><div class="gmail-m_7396323588406062858gmail_signature"><br></div></div><div class="gmail-m_7396323588406062858gmail_signature" style="font-size:12.8px"><div style="font-size:12.8px">==============================<wbr>==============================<wbr>=====================</div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px"><div># Proposal: <span class="gmail-m_7396323588406062858gmail-il">Split</span> <span class="gmail-m_7396323588406062858gmail-il">extension</span> <span class="gmail-m_7396323588406062858gmail-il">into</span><wbr> implementing methods and adding methods and protocols retrospectively</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>## Revision history</div><div><br></div><div>| Version | Date               | Comment       |</div><div>|---------|--------------|----<wbr>----------|</div><div>| Draft 1   | 11 April 2017 | Initial version |</div><div>| Draft 2  | 13 April 2017 | Added support for post-hoc conformance to a protocol - replaced static final <span class="gmail-m_7396323588406062858gmail-il">extensions</span> with final <span class="gmail-m_7396323588406062858gmail-il">extensions</span> |</div><div>| Draft 3 | 17 April 2017 | Added justification section |</div></span><div>| Draft 4 | 2 May 2017   | Allow final <span class="gmail-m_7396323588406062858gmail-il">extensions</span> to be public and allow ad-hoc code reuse |</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>## Introduction</div><div><br></div><div>Currently <span class="gmail-m_7396323588406062858gmail-il">extension</span> methods are confusing because they have different dispatch rules for the same calling syntax. EG:</div><div><br></div><div>    public protocol P {</div><div>        func mP() -&gt; String</div><div>     }</div><div>    <span class="gmail-m_7396323588406062858gmail-il">extension</span> P {</div><div>        func mP() -&gt; String { return &quot;P.mP&quot; }</div><div>        func mE() -&gt; String { return &quot;P.mE&quot; }</div><div>    }</div><div>    struct S: P {</div><div>        func mP() -&gt; String { return &quot;S.mP&quot; }</div><div>        func mE() -&gt; String { return &quot;S.mE&quot; }</div><div>    }</div><div>    let s = S()</div><div>    s.mP() // S.mP as expected</div><div>    s.mE() // S.mE as expected</div><div>    let p: P = s // Note: s now typed as P</div><div>    p.mP() // S.mP as expected</div><div>    p.mE() // P.mE unexpected!</div><div>    </div></span><div>The situation with classes is even more confusing:</div><div><br></div><div>    class C: P { /*gets the protocol <span class="gmail-m_7396323588406062858gmail-il">extensions</span>*/ }</div><div>    let pC: P = C()</div><div>    pC.mP() // P.mP as expected!</div><div>    pC.mE() // P.mE as expected!</div><div>    class D: C {</div><div>        /*override not allowed!*/ func mP() -&gt; String { return &quot;D.mP&quot; }</div><div>        /*override not allowed!*/ func mE() -&gt; String { return &quot;D.mE&quot; }</div><div>    }</div><div>    let pD: P = D()</div><div>    pD.mP() // P.mP unexpected!</div><div>    pD.mE() // P.mE unexpected!</div><div><br></div><div>This proposal cures the above two problem by separating <span class="gmail-m_7396323588406062858gmail-il">extension</span> methods <span class="gmail-m_7396323588406062858gmail-il">i<wbr>nto</span> two seperate use cases: implementations for methods and adding methods and protocols retrospectively. The proposal still retains retroactively adding protocol conformance and ad-hoc code reuse, however these are made easy to understand and safe. </div><div><br></div><div>## Implementing methods in same file as type declaration </div><div><br></div><div>If the <span class="gmail-m_7396323588406062858gmail-il">extension</span> is in the **same** file as the type declaration then its implemented methods are dispatched using a Vtable for protocols and classes and statically for structs and enums. EG:</div><div><br></div><div>File P.swift</div><div><br></div><div>    protocol P {</div><div>        // func m() not declared in type since it is added by the <span class="gmail-m_7396323588406062858gmail-il">extension</span>, under this proposal it is an error to include a declaration in a type **and** in an <span class="gmail-m_7396323588406062858gmail-il">extension</span></div><div>    }</div><div>    <span class="gmail-m_7396323588406062858gmail-il">extension</span> P {</div><div>        func m() { print(&quot;P.m&quot;) } // m is added to the protocol declaration</div><div>    }</div><div><br></div><div>Same or another file</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>    struct S: P {</div><div>        override func m() { print(&quot;S.m&quot;) } // Note override required because m already has an implementation from the <span class="gmail-m_7396323588406062858gmail-il">extension</span></div><div>    }</div><div>    let p: P = S() // Note typed as P</div><div>    p.m() // Now prints S.m as expected </div><div><br></div></span><div><span class="gmail-m_7396323588406062858gmail-il">Extensions</span> in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations. Ad-hoc code reuse is supported, in particular if a class/enum/strict already had a method, m say, and a protocol, P say, required an m then an <span class="gmail-m_7396323588406062858gmail-il">extension</span> that added P would not need to provide m (i.e. as at present).</div><div><br></div><div>In a protocol at present you can declare a method that is then implemented in an <span class="gmail-m_7396323588406062858gmail-il">extension</span> without the use of the override keyword. This situation only applies to protocols, for structs/enumerated/classes you cannot declare in type and implement in an <span class="gmail-m_7396323588406062858gmail-il">extension</span> at all. This proposal unifies the behaviour of protocol/struct/enum/class with <span class="gmail-m_7396323588406062858gmail-il">extensions</span> and also prevents the error of a minor typo between the protocol and <span class="gmail-m_7396323588406062858gmail-il">extension</span> adding two methods instead of generating an error, by requiring either:</div><div><br></div><div>  1. The method is only declared in the protocol and not in any <span class="gmail-m_7396323588406062858gmail-il">extensions</span> and is therefore abstract</div><div>  2. The method is only in one <span class="gmail-m_7396323588406062858gmail-il">extension</span> and not in the protocol</div><div><br></div><div>A method can be abstract in one protocol and implemented in a second protocol that extends the first. </div><div><br></div><div>The implementation needed to achieve this proposal for a protocol is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and its address passed/copied (i.e. it becomes a class instance). No change is needed for a class instance typed as a protocol, which unlike at present can now be passed/copied as a protocol directly. Think of a protocol as like an abstract class; cannot be instantiated like an abstract class and which possibly has abstract methods, but in different in that it cannot have fields but can be multiply inherited. </div><div><br></div><div>Static and final methods implemented in <span class="gmail-m_7396323588406062858gmail-il">extensions</span> are not part of the Vtable and are statically dispatched, i.e. no change from current Swift for static but final now has the expected meaning for a protocol. Dispatching for structs and classes unchanged.</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>## Retrospectively adding protocols and methods</div><div><br></div></span><div>A new type of <span class="gmail-m_7396323588406062858gmail-il">extension</span> is proposed, a `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>`, which can be either in or outside the file in which the protocol/struct/enum/class declaration is in:</div><div><br></div><div>File P.swift</div><div><br></div><div>    protocol P {}</div><div>    <span class="gmail-m_7396323588406062858gmail-il">extension</span> P {</div><div>        func m() { print(&quot;P.m&quot;) } // m is added to the protocol declaration</div><div>    }</div><div><br></div><div>Same or another file</div><div><br></div><div>    struct S: P {} // Inherits m from the <span class="gmail-m_7396323588406062858gmail-il">extension</span> </div><div><br></div><div>In file P2.swift</div><div><br></div><div>    protocol P2 {</div><div>        func m2()</div><div>        func m() // Note same signature as P.m which S already implements</div><div>    }</div><div>    </div><div>In same or another file</div><span class="gmail-m_7396323588406062858gmail-im"><div>    </div><div>    final <span class="gmail-m_7396323588406062858gmail-il">extension</span> S: P2 { // Note <span class="gmail-m_7396323588406062858gmail-il">extension</span> marked final</div></span><div>        // m cannot be provided because S already has a final m (the inherited method must be final)</div><div>        func m2() { print(&quot;SP2.m2&quot;) } // Implicitly final, completes implementation of P2</div><div>        func mE() { print(&quot;SP2.mE&quot;) } // Implicitly final, not an existing method</div><span class="gmail-m_7396323588406062858gmail-im"><div>    }</div><div><br></div><div>Which are called as any other method would be called:</div><div><br></div></span><div>    let s = S() // or S() as P2 or s: P2</div><div>    s.m() // Prints S.m</div><div>    s.m2() // Prints SP2.m2</div><div>    s.mE() // Prints SP2.mE</div><div><br></div><div>Notes:</div><div><br></div><div>  1. A method added by a `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>`, e.g. `mE`, is implicitly final (as the name would suggest). </div><div><br></div><div>  2. If the `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` adds a method, e.g. `mE`, that method cannot already exist. IE a `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` cannot override an existing method or implement a protocol declared method that lacks an implementation (unless it also adds the protocol). This is retroactively adding a method. Also see next point.</div><div><br></div><div>  3. If the `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` adds a protocol, e.g. `P2`, then it must implement all the methods in that protocol that are not  implemented, e.g. `m2`. This is retroactively adding protocol conformance. Also see next point.</div><div><br></div><div>  4. If the `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` adds a protocol, e.g. `P2`, then it inherits all the methods in that protocol that are implemented, e.g. `m`. These inherited methods must be final. This is ad-hoc code reuse of final methods when retroactively adding protocol conformance. </div><div><br></div><div>Final-<span class="gmail-m_7396323588406062858gmail-il">extensions</span> can have `where` clauses. </div><div><br></div><div>The implementation for a `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` is always static dispatching. That is why all methods involved in a `final <span class="gmail-m_7396323588406062858gmail-il">extension</span>` are final. The compiler always knows that the method can be called statically and there is no need for a Vtable entry for any of the methods, it is as though the methods were declared static but with the more convenient syntax of a normal method. </div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>## Justification </div><div><br></div><div>The aim of Swift is nothing more than dominating the world. Using the current, April 2017, <a href="https://www.tiobe.com/tiobe-index/" target="_blank">https://www.tiobe.com/<wbr>tiobe-index/</a><div style="display:inline-block;width:16px;height:16px"></div> index of job adverts for programmers the languages that are in demand are: Java 15.568%, C 6.966%, C++ 4.554%, C# 3.579%, Python 3.457%, PHP 3.376%, Visual Basic .NET 3.251%, JavaScript 2.851%, Delphi/Object Pascal 2.816%, Perl 2.413%, Ruby 2.310%, and Swift 2.287%. So Swift at 12th is doing very well for a new language and is already above Objective-C at 14th. However there is obviously a long way to go and the purpose of this proposal is to help with this climb.</div><div><br></div></span><div>A characteristic of many of the languages above Swift in the Tiobe Index is that they have major third party libraries; for some languages they are almost defined by their third part libraries, e.g. Ruby for Rails. A major part of this proposal is to make <span class="gmail-m_7396323588406062858gmail-il">extensions</span> safe when using multiple libraries from different venders. In particular, the two forms of <span class="gmail-m_7396323588406062858gmail-il">extensions</span> in this proposal can safely be exported.</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>As part of Swift&#39;s goal of world domination is that it is meant to be easy to learn by a process of &quot;successive disclosure&quot;. The current inconsistent behaviour of protocols and <span class="gmail-m_7396323588406062858gmail-il">extensions</span> hinders this process and is a common gotcha for newbies. This proposal eliminates that problem also.</div><div><br></div></span><div><span class="gmail-m_7396323588406062858gmail-il">Extensions</span> are not new in languages, they are part of the .NET languages for example. Since .NET popularised <span class="gmail-m_7396323588406062858gmail-il">extensions</span> they have been discussed by other language communities, particularly Java and Scala, and in the academic community (normally termed the Expression Problem) however they have not proved popular because of the problems they cause. Nearly all languages have a strong bias towards keeping the language small and simple and trade of the advantages of a feature against the disadvantages. The feature only makes it <span class="gmail-m_7396323588406062858gmail-il">into</span> the language if it offers many advantages, has few disadvantages, and is not heavily overlapping with other features. It is this keeping it small and simple test that <span class="gmail-m_7396323588406062858gmail-il">extensions</span> have failed in other languages, in particular their behaviour is hard to predict in a large code base with multiple third party libraries.</div><div><br></div><div>However, <span class="gmail-m_7396323588406062858gmail-il">extensions</span> are popular in Swift and this proposals makes a few changes to them to make their behaviour predictable both in terms of third party libraries and in terms of method dispatch when the variable is typed as a protocol. Thereby still providing <span class="gmail-m_7396323588406062858gmail-il">extensions</span> including retroactive conformance and ad-hoc code reuse, but without the problems.</div><span class="gmail-m_7396323588406062858gmail-im"><div><br></div><div>## Possible future work (not part of this proposal)</div><div><br></div><div>This proposal will naturally allow bodies to be added to protocols directly rather than via an <span class="gmail-m_7396323588406062858gmail-il">extension</span>, since under the proposal the <span class="gmail-m_7396323588406062858gmail-il">extension</span>adds the declaration to the type so it is a small step to allow the protocol methods to have an implementation. </div><div><br></div><div>In an opposite sense to the above adding bodies to protocols, <span class="gmail-m_7396323588406062858gmail-il">extensions</span> could be allowed to add method declarations without bodies to protocols. </div><div><br></div><div>The two above future work proposals, if both added, would add symmetry to where declarations and bodies may appear for protocols. </div><div><br></div><div>## In summary</div><div><br></div></span><div>The proposal formalises the <span class="gmail-m_7396323588406062858gmail-il">split</span> use of <span class="gmail-m_7396323588406062858gmail-il">extensions</span> <span class="gmail-m_7396323588406062858gmail-il">into</span> their two uses: implementing methods and retroactively adding protocols and methods (in both cases including ad-hoc code reuse). The purpose of this <span class="gmail-m_7396323588406062858gmail-il">split</span> is to eliminate the problems associated with exceptions that have been well documented both with respect to Swift and other languages. Syntax is added that clarifies their two use cases (implementing methods and retroactively adding):</div><div><br></div><div>  1. The former are termed <span class="gmail-m_7396323588406062858gmail-il">extensions</span> and must be in the same file as the type is declared, but can have non-final or final methods.</div><div>  2. The latter are termed final-<span class="gmail-m_7396323588406062858gmail-il">extensions</span> and can be in any file, however final-<span class="gmail-m_7396323588406062858gmail-il">extensions</span> only have final methods.</div><div><br></div><div>Note the distinction between an <span class="gmail-m_7396323588406062858gmail-il">extension</span> in the same file and in a separate file is consistent with the philosophy that there is special status to the same file as proposed for private in &lt;<a href="http://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md" target="_blank">http://github.com/apple/swift<wbr>-evolution/blob/master/proposa<wbr>ls/0169-improve-interaction-<wbr>between-private-declarations-<wbr>and-<span class="gmail-m_7396323588406062858gmail-il">extensions</span>.md</a><div style="display:inline-block;width:16px;height:16px"></div>&gt;.</div></div><div style="font-size:12.8px"><br></div><div style="font-size:12.8px">==============================<wbr>==============================<wbr>=====================</div></div></div></div><div class="gmail_extra"><br clear="all"><div><div class="gmail_signature" data-smartmail="gmail_signature">  -- Howard.<br></div></div>
<br><div class="gmail_quote">On 17 May 2017 at 07:41, Jordan Rose via swift-users <span dir="ltr">&lt;<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><span class=""><br><div><blockquote type="cite"><div>On May 16, 2017, at 10:44, Charles Srstka via swift-users &lt;<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a>&gt; wrote:</div><br class="m_3881848328634709972Apple-interchange-newline"><div><div style="word-wrap:break-word"><blockquote type="cite">On May 16, 2017, at 12:32 PM, Nevin Brackett-Rozinsky via swift-users &lt;<a href="mailto:swift-users@swift.org" target="_blank">swift-users@swift.org</a>&gt; wrote:<br></blockquote><div><blockquote type="cite"><br class="m_3881848328634709972Apple-interchange-newline"><div><span 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;float:none;display:inline!important">There is not.</span><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">At some point in the future, I believe the plan is to eventually allow default implementations to be written in-line within the protocol declaration itself. In other words, the fact that default implementations currently must appear in extensions, is a temporary limitation that has not yet been a priority to address.</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">Once we gain the ability to define default implementations within protocols themselves, rather than extensions, then your use-case will “just work” (at least as long as you control the protocol anyway). I wouldn’t hold my breath though, as that behavior will not appear this year, and the plans for next year have not been hashed out yet.</div></div></blockquote></div><br><div>Even that won’t completely solve the problem, though, because:</div><div><br></div><div>protocol P {</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">        </span>func foo() {</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">                </span>// default implementation</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">        </span>}</div><div>}</div><div><br></div><div>struct S: P {</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">        </span>func foo() {</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">                </span>// overriden implementation</div><div><span class="m_3881848328634709972Apple-tab-span" style="white-space:pre-wrap">        </span>}</div><div>}</div><div><br></div><div>If foo is refactored here, S will start getting the wrong implementation of it, silently, with no warning.</div><div><br></div><div>People have tried to bring up proposals to add some sort of “override”-like keyword for protocols on swift-evolution a bunch of times, but it always gets shouted down by certain members of the group, so we’re probably permanently stuck with this situation where a supposedly “protocol-oriented” language is not safe to use with protocols.</div></div></div></blockquote><br></div></span><div>I object to this characterization. People have tried to bring up such proposals, and it is invariably pointed out (usually by the same group of us) that these proposals don&#39;t discuss retroactive modeling, and then the thread dies. I can&#39;t remember a case where the proposal author actually incorporates this feedback into their proposal to handle retroactive modeling, or extension members of one protocol being used as the default implementation of another protocol.</div><div><br></div><div>(This just means the problem space is more difficult than the proposer initially thought, and they&#39;re not going to take that on right now, which is fine. It&#39;s a learning experience; designing features that interact well with the whole language is not easy! We should probably add this to the &quot;commonly proposed&quot; list, though, so that we don&#39;t keep retreading that initial ground.)</div><div><br></div><div>Jordan</div><br></div><br>______________________________<wbr>_________________<br>
swift-users mailing list<br>
<a href="mailto:swift-users@swift.org">swift-users@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-users" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-users</a><br>
<br></blockquote></div><br></div>