<div dir="ltr">On Fri, Sep 8, 2017 at 4:00 PM, Itai Ferber 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 style="word-wrap:break-word;line-break:after-white-space"><br><div><span class=""><br><blockquote type="cite"><div>On Sep 8, 2017, at 12:46 AM, Haravikk via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br class="m_-2897067699292549448Apple-interchange-newline"><div><div style="word-wrap:break-word"><br><div><blockquote type="cite"><div>On 7 Sep 2017, at 22:02, Itai Ferber <<a href="mailto:iferber@apple.com" target="_blank">iferber@apple.com</a>> wrote:</div><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><pre class="m_-2897067699292549448language-swift" style="margin-top:0.5em;margin-bottom:0.5em;background-color:rgb(245,242,240);border:1px solid rgb(204,204,204);font-size:13px;line-height:1.5;overflow:auto;padding:1em;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;font-family:'SF Mono',Menlo,Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;white-space:pre-wrap;word-break:normal;word-wrap:normal"><code class="m_-2897067699292549448language-swift" style="margin:0px;padding:0px;border:none;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;background-image:none;font-family:'SF Mono',Menlo,Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5">protocol <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Fooable</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Equatable</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">{ // Equatable is just a simple example</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">var</span> myFoo<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Int</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">{</span> <span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">get</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">}</span>
<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">}</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">extension</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Fooable</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">{</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">static</span> <span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">func</span> <span class="m_-2897067699292549448token m_-2897067699292549448operator" style="color:rgb(166,127,89);background-color:rgba(255,255,255,0.498039)">==</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">(</span><span class="m_-2897067699292549448token m_-2897067699292549448number" style="color:rgb(153,0,85)">_</span> lhs<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">Self</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">,</span> <span class="m_-2897067699292549448token m_-2897067699292549448number" style="color:rgb(153,0,85)">_</span> rhs<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">Self</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">)</span> <span class="m_-2897067699292549448token m_-2897067699292549448operator" style="color:rgb(166,127,89);background-color:rgba(255,255,255,0.498039)">-</span><span class="m_-2897067699292549448token m_-2897067699292549448operator" style="color:rgb(166,127,89);background-color:rgba(255,255,255,0.498039)">></span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Bool</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">{</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">return</span> lhs<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">.</span>myFoo <span class="m_-2897067699292549448token m_-2897067699292549448operator" style="color:rgb(166,127,89);background-color:rgba(255,255,255,0.498039)">==</span> rhs<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">.</span>myFoo
<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">}</span>
<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">}</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">struct</span> X <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Fooable</span> <span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">{</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">let</span> myFoo<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">Int</span>
<span class="m_-2897067699292549448token m_-2897067699292549448keyword" style="color:rgb(0,119,170)">let</span> myName<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448builtin" style="color:rgb(102,153,0)">String</span>
<span class="m_-2897067699292549448token m_-2897067699292549448comment" style="color:rgb(112,128,144)">// Whoops, forgot to give an implementation of ==</span>
<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">}</span>
<span class="m_-2897067699292549448token m_-2897067699292549448function" style="color:rgb(221,74,104)">print</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">(</span><span class="m_-2897067699292549448token m_-2897067699292549448function" style="color:rgb(221,74,104)">X</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">(</span>myFoo<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448number" style="color:rgb(153,0,85)">42</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">,</span> myName<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448string" style="color:rgb(102,153,0)">"Alice"</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">)</span> <span class="m_-2897067699292549448token m_-2897067699292549448operator" style="color:rgb(166,127,89);background-color:rgba(255,255,255,0.498039)">==</span> <span class="m_-2897067699292549448token m_-2897067699292549448function" style="color:rgb(221,74,104)">X</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">(</span>myFoo<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448number" style="color:rgb(153,0,85)">42</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">,</span> myName<span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">:</span> <span class="m_-2897067699292549448token m_-2897067699292549448string" style="color:rgb(102,153,0)">"Bob"</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">)</span><span class="m_-2897067699292549448token m_-2897067699292549448punctuation" style="color:rgb(153,153,153)">)</span> <span class="m_-2897067699292549448token m_-2897067699292549448comment" style="color:rgb(112,128,144)">// true</span></code></pre><div>This property is<span class="m_-2897067699292549448Apple-converted-space"> </span><i>necessary</i>, but not<span class="m_-2897067699292549448Apple-converted-space"> </span><i>sufficient</i><span class="m_-2897067699292549448Apple-converted-space"> </span>to provide a correct implementation. A default implementation might be able to<span class="m_-2897067699292549448Apple-converted-space"> </span><i>assume</i> something about the types that it defines, but it does not necessarily know enough.</div></div></div></div></blockquote><div><br></div><div>Sorry but that's a bit of a contrived example; in this case the protocol should <b>not</b> implement the equality operator if more information may be required to define equality. It should only be implemented if the protocol is absolutely clear that .myFoo is the only part of a Fooable that can or should be compared as equatable, e.g- if a Fooable is a database record and .myFoo is a primary key, the data could differ but it would still be a reference to the same record.</div><div><br></div><div>To be clear, I'm not arguing that someone can't create a regular default implementation that also makes flawed assumptions, but that synthesised/reflective implementations <b>by their very nature have to</b>, as they cannot under every circumstance guarantee correctness when using parts of a concrete type that they know nothing about.</div></div></div></div></blockquote></span><div>You can’t argue this both ways:</div><div><ul class="m_-2897067699292549448MailOutline"><li>If you’re arguing this on principle, that in order for synthesized implementations to be correct, they <i>must</i> be able to — <i>under every circumstance</i> — guarantee correctness, then you have to apply the same reasoning to default protocol implementations. Given a default protocol implementation, it is possible to come up with a (no matter how contrived) case where the default implementation is wrong. Since you’re arguing this <i>on principle</i>, you cannot reject contrived examples.</li><li>If you are arguing this <i>in practice</i>, then you’re going to have to back up your argument with evidence that synthesized examples are more often wrong than default implementations. You can’t declare that synthesized implementations are <i>by nature</i> incorrect but allow default implementations to slide because <i>in practice</i>, many implementations are allowable. There’s a reason why synthesis passed code review and was accepted: in the majority of cases, synthesis was deemed to be beneficial, and would provide correct behavior. If you are willing to say that yes, sometimes default implementations are wrong but overall they’re correct, you’re going to have to provide hard evidence to back up the opposite case for synthesized implementations. You stated in a previous email that "<span style="font-family:SFHello-Regular">A synthesised/reflective implementation however may return a result that is simply incorrect, because it is based on assumptions made by the protocol developer, with no input from the developer of the concrete type. In this case the developer must override it in to provide </span><b style="font-family:SFHello-Regular">correct</b><font face="SFHello-Regular"> behaviour." — if you can back this up with evidence (say, taking a survey of a large number of model types and see if in the majority of cases synthesized implementation would be incorrect) to provide a compelling argument, then this is something that we should in that case reconsider.</font></li></ul></div></div></div></blockquote><div><br></div><div>Well put, and I agree with this position 100%. However, to play devil's advocate here, let me summarize what I think Haravikk is saying:</div><div><br></div><div>I think the "synthesized" part of this is a red herring, if I understand Haravikk's argument correctly. Instead, it is this:</div><div><br></div><div>(1) In principle, it is possible to have a default implementation for a protocol requirement that produces the correct result--though not necessarily in the most performant way--for all possible conforming types, where by conforming we mean that the type respects both the syntactic requirements (enforced by the compiler) and the semantic requirements (which may not necessarily be enforceable by the compiler) of the protocol in question.</div><div><br></div><div>(2) However, there exist *some* requirements that, by their very nature, cannot have default implementations which are guaranteed to produce the correct result for all conforming types. In Haravikk's view, no default implementations should be provided in these cases. (I don't necessarily subscribe to this view in absolute terms, but for the sake of argument let's grant this premise.)</div><div><br></div><div>(3) Equatable, Hashable, and Codable requirements are, by their very nature, such requirements that cannot have default implementations guaranteed to be correct for all conforming types. Therefore, they should not have a default implementation. It just so happens that a default implementation cannot currently be written in Swift itself and must be synthesized, but Haravikk's point is that even if they could be written in native Swift through a hypothetical reflection facility, they should not be, just as many other protocol requirements currently could have default implementations written in Swift but should not have them because they cannot be guaranteed to produce the correct result.</div><div><br></div><div>My response to this line of argumentation is as follows:</div><div><br></div><div>For any open protocol (i.e., a protocol for which the universe of possible conforming types cannot be enumerated a priori by the protocol designer) worthy of being a protocol by the Swift standard ("what useful thing can you do with such a protocol that you could not without?"), any sufficiently interesting requirement (i.e., one for which user ergonomics would measurably benefit from a default implementation) either cannot have a universally guaranteed correct implementation or has an implementation which is also going to be the most performant one (which can therefore be a non-overridable protocol extension method rather than an overridable protocol requirement with a default implementation). </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 style="word-wrap:break-word;line-break:after-white-space"><div><span class=""><blockquote type="cite"><div><div style="word-wrap:break-word"><div><blockquote type="cite"><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"><span style="font-family:SFHello-Regular">Reflective/synthesised default implementations</span> <b style="font-family:SFHello-Regular">must by their very nature</b><span style="font-family:SFHello-Regular"> make assumptions about a concrete type that are</span> <b style="font-family:SFHello-Regular">not</b> <span style="font-family:SFHello-Regular">cannot be guaranteed to be correct. The properties and methods they may end up interacting with</span><b style="font-family:SFHello-Regular">may have nothing at all to do with the protocol</b><span style="font-family:SFHello-Regular">. Equatable remains by far the simplest example; just because a developer has used equatable properties does</span> <b style="font-family:SFHello-Regular">not</b><span style="font-family:SFHello-Regular"> guarantee that all of them should be compared during a check for equality.</span><br></blockquote><div>In the same way that you might consider synthesized conformances to overreach into a type and touch things which are not related to a protocol, default implementations can be considered<span class="m_-2897067699292549448Apple-converted-space"> </span><i>underreach</i> in that they don’t know anything about properties which are necessary for providing a correct implementation.</div></div></blockquote><div><br></div><div>If more information is necessary to provide a correct implementation, then a default implementation shouldn't be provided. This is what unimplemented properties and methods are for; either getting the developer to provide the missing information, or getting them to implement the correct behaviour.</div></div></div></div></blockquote></span><div>I agree, but you can’t selectively argue this.</div><span class=""><br><blockquote type="cite"><div>______________________________<wbr>_________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br></div></blockquote></span></div><br></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>