<div dir="ltr">On Wed, Aug 24, 2016 at 3:28 PM, Karl 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"><br><div><blockquote type="cite"><div><div class="h5"><div>On 24 Aug 2016, at 20:38, Douglas Gregor via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br></div></div><div><div><div class="h5"><div style="font-family:Helvetica;font-size:12px;font-style: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"><div><br>On Aug 22, 2016, at 9:59 PM, Jonathan Hull via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>> wrote:</div><br><div><div>Hi everyone,<br><br>We talked about this before when we were discussing mixins, and there seemed to be generally positive feelings towards it as a feature for the future. I am fairly certain this affects the ABI though, so I thought I would bring it up now.<br><br>If two protocols have methods/properties with the same name, but different signatures, we need a way to distinguish between them when attempting to conform to both.<br><br><span style="white-space:pre-wrap">        </span>protocol A {<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>var x:Int {get set}<br><span style="white-space:pre-wrap">        </span>}<br><br><span style="white-space:pre-wrap">        </span>protocol B {<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>var x:Double {get set}<br><span style="white-space:pre-wrap">        </span>}<br></div></div></blockquote><div><br></div><div>I believe that this can happen, and it is unfortunate that Swift has no mechanism for dealing with it today. However, I agree with Xiaodi that your proposal would be much stronger with real-world examples rather than theoretical ones.</div><br><blockquote type="cite"><div><div>One possibility is to allow a struct/class/enum to conform to the protocol while renaming one (or both) of the clashing methods:<br><br><span style="white-space:pre-wrap">        </span>struct C: A,B {<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>var x:Int<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>var y:Double implements B.x<br><span style="white-space:pre-wrap">        </span>}<br><br>The conforming method/property would still have to have the same signature, but could have a different name (and parameter labels). It would also allow protocol methods which have identical signatures and semantics, but different names to be implemented using the same method (i.e ‘implements D.z & E.w’).<br><br>When something is cast to the protocol (say ‘as B’), then calling the property (e.g. ‘x’) would end up calling the implementation of the renamed property ( ‘y’ in this example) on the conforming type.<br></div></div></blockquote><div><br></div><div>Sure. Calling through the protocol type will get whatever method/property satisfied the protocol requirement. Yes, there are limits here due to protocols with associated types and Self requirements, but I fully expect those to go away at some point with generalized existentials.</div><div><br></div><blockquote type="cite"><div><div>I think we would also want a way to retroactively conform using existing properties/methods in an extension declaring conformance. Not sure what the best syntax for that would be. Off the top of my head (though I would love to have something with less cruft):<br><br><span style="white-space:pre-wrap">        </span>extension D:B {<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>@conform(to: B.x, with: D.y)<br><span style="white-space:pre-wrap">        </span>}<br><br>or maybe just:<br><span style="white-space:pre-wrap">        </span><br><span style="white-space:pre-wrap">        </span>extension D:B {<br><span style="white-space:pre-wrap">        </span><span style="white-space:pre-wrap">        </span>D.y implements B.x<br><span style="white-space:pre-wrap">        </span>}<br><span style="white-space:pre-wrap">        </span><br><br>All of this is merely to start the discussion, so feel free to propose better syntax or a more elegant solution...<br></div></div></blockquote></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><span style="font-family:Helvetica;font-size:12px;font-style: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">C# has a much narrower solution that lets you qualify the method declaration (rather than doing a full rename), e.g.,</span><div style="font-family:Helvetica;font-size:12px;font-style: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-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span>struct C : A {</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> var x: Int</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> var y: Double</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span>}</div><div style="font-family:Helvetica;font-size:12px;font-style: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-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span>extension C : B {</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> var B.x: Double {</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> get { return y }</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> set { y = newValue }</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span> }</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="white-space:pre-wrap">        </span>}</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><br></div><div>They have some examples at:</div><div><br><div><span style="white-space:pre-wrap">        </span><a href="https://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx" target="_blank">https://msdn.microsoft.com/en-<wbr>us/library/aa288461(v=vs.71).<wbr>aspx</a></div><div><br></div><div>One would have to figure out what the name-lookup rules are, of course, but this might allow us to solve the problem without introducing a generalized renaming mechanism.</div><div><br></div><div><span style="white-space:pre-wrap">        </span>- Doug</div><div><br></div></div></div></div></div><span class=""><span style="font-family:Helvetica;font-size:12px;font-style: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">______________________________<wbr>_________________</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><span style="font-family:Helvetica;font-size:12px;font-style: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">swift-evolution mailing list</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><a href="mailto:swift-evolution@swift.org" style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank">swift-evolution@swift.org</a><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="font-family:Helvetica;font-size:12px;font-style:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a></span></div></blockquote></div><br><div><br></div><div>+1 to the C# mechanism (I think it’s basically what I proposed earlier). Resolving protocol conformance by name is soooo Objective-C. Swift has a much stronger protocol system, and members which belong to a protocol should **belong** to that protocol (have it mangled in to the name, MyType__MyProtocol__MyMember, etc. As a shorthand, when the corresponding protocol is unambiguous you can declare or use it directly (MyType.MyMember).</div><div><br></div><div>I’m very much against renaming. It’s a hack; we want a solution. That applies to all instances of renaming - here, in protocol conformances, and in general with imported types (there was a proposal some months ago which involved renaming members when importing).</div><div><br></div><div>The two most important things for programmers are how well they know the language, and how well they know the libraries they are using. Renaming makes the latter basically useless. Somebody doesn’t like the long names in Apple’s SDK, and suddenly UITableView becomes Table or even just an emoji (why not?). The code you know and patterns you expect become a bunch of alien hieroglyphics which looked neat and terse to the person writing it but requires extensive study by anybody else.</div><div><br></div><div>Renaming is just bad, bad, bad. I’ll argue strongly against adding any kind of symbol renaming to Swift whatsoever for as long as I’m active in the community (except across language barriers, like with @objc(<name>), because the symbols don’t match up anyway so there’s no prior knowledge to break — in fact it’s usually used to retain older APIs and make transitions *less* breaking).</div></div></blockquote><div><br></div><div>After some reflection, I think I largely agree with Karl on this point. A C#-like explicit implementation mechanism might be the way to go; it solves the issue of colliding names without introducing the difficulties that arise from arbitrary renaming.</div><div><br></div><div>Where I would disagree with Karl would be on mangling the protocol names into member names; as I wrote above, I strongly hold to the view that it's important that protocols guarantee the API of conforming types, so I think that protocol requirements should be in the same namespace as all other type members *unless* something else is explicitly necessary due to a collision. However, in the end, the disagreement here is more conceptual than practical, as everyday use of the feature would be largely similar in any case.</div><div><br></div><div>Based on that view, however, I'd go a step further in simplifying/rationalizing the C#-like mechanism, like so:</div><div><br></div><div>* I'd propose that conformance to a protocol can be either as it is in Swift 3 (let's call it "direct") or it can be what C# would call explicit (let's call it "indirect", just for good measure); unlike C#, this choice would refer to conformance as a whole, not to implementation of specific members (although this is more of a notional difference from C#, since as proposed below, specific members may or may not be accessible directly without casting).</div><div><br></div><div>* Members declared directly on the type that match the requirements of an "indirectly conformed" protocol will satisfy those requirements, but any remaining requirements not satisfied in that manner (for instance, due to colliding names) must be implemented in an extension that explicitly declares indirect conformance. For example:</div><div><br></div><div>```</div><div>protocol A {</div><div> var x: Int { get }</div><div> var y: Int { get }</div><div>}</div><div><br></div><div>protocol B {</div><div> var x: Double { get }</div><div> var y: Int { get }</div><div>}</div><div><br></div><div>struct S : A {</div><div> var x: Int { return 42 }</div><div> var y: Int { return 43 }</div><div> // we cannot have S conform to B just yet</div><div> // because we can't re-declare `x` as a property of type Double</div><div>}</div><div><br></div><div>extension S : indirect B {</div><div> var x: Double { return -42 }</div><div> // note that `y` is satisfied by the implementation above</div><div>}</div><div>```</div><div><br></div><div>* Members declared in an extension adding an "indirect" conformance to a protocol can only be accessed after the type is cast to the corresponding existential type. In the example above, `S.x` is an Int (and is the same as `(S as A).x`) and has value 42, but `(S as B).x` is a Double and has value -42.0. Meanwhile, `S.y` can be accessed directly or indirectly as either `(S as A).y` or `(S as B).y` and has value 43.</div><div><br></div><div>* Until generics are completed, only protocols without Self or associated type requirements can be used as an existential, so for now let's limit indirect conformances to protocols without Self or associated type requirements; this limit should be relaxed in tandem with the implementation of generalized existentials.</div><div><br></div><div>Would this simplified scheme go far enough in addressing the original issue?</div><div><br></div><div><br></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"><div></div><div>Karl</div></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>