<div dir="ltr"><div class="gmail_quote"><div dir="ltr">On Mon, May 2, 2016 at 1:25 PM Dave Abrahams via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
on Mon May 02 2016, Tony Allevato &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br>
<br>
&gt; Open issue: Class types and inheritance<br>
&gt;<br>
&gt; While this approach works well for value types, these static operators may not<br>
&gt; work as expected for class types when inheritance is involved, and more work may<br>
&gt; be needed here.<br>
&gt;<br>
&gt; We can currently model the behavior we&#39;d like to achieve by using a named eq<br>
&gt; method instead of the operator itself. (Note that we are not proposing that the<br>
&gt; function be named eq in the final design; this was done simply to perform the<br>
&gt; experiment with today&#39;s compiler.) Then we implement both the new method and the<br>
&gt; current == operator and compare their behaviors. For example:<br>
&gt;<br>
&gt; protocol ProposedEquatable {<br>
&gt;   static func eq(lhs: Self, _ rhs: Self) -&gt; Bool<br>
&gt; }<br>
&gt;<br>
&gt; class Base: ProposedEquatable, Equatable {<br>
&gt;   static func eq(lhs: Base, _ rhs: Base) -&gt; Bool {<br>
&gt;     print(&quot;Base.eq&quot;)<br>
&gt;     return true<br>
&gt;   }<br>
&gt; }<br>
&gt; func ==(lhs: Base, rhs: Base) -&gt; Bool {<br>
&gt;   print(&quot;==(Base, Base)&quot;)<br>
&gt;   return true<br>
&gt; }<br>
&gt;<br>
&gt; class Subclass: Base {<br>
&gt;   static func eq(lhs: Subclass, _ rhs: Subclass) -&gt; Bool {<br>
&gt;     print(&quot;Subclass.eq(Subclass, Subclass)&quot;)<br>
&gt;     return true<br>
&gt;   }<br>
&gt; }<br>
&gt; func ==(lhs: Subclass, rhs: Subclass) -&gt; Bool {<br>
&gt;   print(&quot;==(Subclass, Subclass)&quot;)<br>
&gt;   return true<br>
&gt; }<br>
&gt;<br>
&gt; func eq&lt;T: ProposedEquatable&gt;(lhs: T, _ rhs: T) -&gt; Bool {<br>
&gt;   return T.eq(lhs, rhs)<br>
&gt; }<br>
&gt;<br>
&gt; let x = Subclass()<br>
&gt; let y = Subclass()<br>
&gt; let z = y as Base<br>
&gt;<br>
&gt; eq(x, y)  // prints &quot;Base.eq&quot;<br>
&gt; eq(x, z)  // prints &quot;Base.eq&quot;<br>
&gt;<br>
&gt; x == y    // prints &quot;==(Subclass, Subclass)&quot;<br>
&gt; x == z    // prints &quot;==(Base, Base)&quot;<br>
&gt;<br>
&gt; The result of eq(x, y) was a bit surprising, since the generic argument T is<br>
&gt; bound to Subclass and there should be no dynamic dispatch at play there. (Is the<br>
&gt; issue that since Base is the class explicitly conforming to ProposedEquatable,<br>
&gt; this is locking in Self being bound as Base, causing that overload to be found<br>
&gt; in the compiler&#39;s search? Or is this a bug?)<br>
&gt;<br>
&gt; An attempt was also made to fix this using dynamic dispatch, by implementing eq<br>
&gt; as a class method instead of astatic method:<br>
&gt;<br>
&gt; protocol ProposedEquatable {<br>
&gt;   static func eq(lhs: Self, _ rhs: Self) -&gt; Bool<br>
&gt; }<br>
&gt;<br>
&gt; class Base: ProposedEquatable, Equatable {<br>
&gt;   class func eq(lhs: Base, _ rhs: Base) -&gt; Bool {<br>
&gt;     print(&quot;Base.eq&quot;)<br>
&gt;     return true<br>
&gt;   }<br>
&gt; }<br>
&gt; func ==(lhs: Base, rhs: Base) -&gt; Bool {<br>
&gt;   print(&quot;==(Base, Base)&quot;)<br>
&gt;   return true<br>
&gt; }<br>
&gt;<br>
&gt; class Subclass: Base {<br>
&gt;   override class func eq(lhs: Base, _ rhs: Base) -&gt; Bool {<br>
&gt;     print(&quot;Subclass.eq(Base, Base)&quot;)<br>
&gt;     return true<br>
&gt;   }<br>
&gt;   class func eq(lhs: Subclass, _ rhs: Subclass) -&gt; Bool {<br>
&gt;     print(&quot;Subclass.eq(Subclass, Subclass)&quot;)<br>
&gt;     return true<br>
&gt;   }<br>
&gt; }<br>
&gt; func ==(lhs: Subclass, rhs: Subclass) -&gt; Bool {<br>
&gt;   print(&quot;==(Subclass, Subclass)&quot;)<br>
&gt;   return true<br>
&gt; }<br>
&gt;<br>
&gt; func eq&lt;T: ProposedEquatable&gt;(lhs: T, _ rhs: T) -&gt; Bool {<br>
&gt;   return T.eq(lhs, rhs)<br>
&gt; }<br>
&gt;<br>
&gt; let x = Subclass()<br>
&gt; let y = Subclass()<br>
&gt; let z = y as Base<br>
&gt;<br>
&gt; eq(x, y)  // prints &quot;Subclass.eq(Base, Base)&quot;<br>
&gt; eq(x, z)  // prints &quot;Base.eq&quot;<br>
&gt;<br>
&gt; x == y    // prints &quot;==(Subclass, Subclass)&quot;<br>
&gt; x == z    // prints &quot;==(Base, Base)&quot;<br>
&gt;<br>
&gt; This helped slightly, since at least it resulting in a method on the expected<br>
&gt; subclass being called, but this still means that anyone implementing this<br>
&gt; operator on subclasses would have to do some casting, and it&#39;s awkward that<br>
&gt; subclasses would be expected to write its operator in terms of the conforming<br>
&gt; base class.<br>
&gt;<br>
&gt; It should also be noted (code not provided here) that using instance methods<br>
&gt; does not solve this problem, presumably for the same dispatch-related reasons<br>
&gt; that the class methods called the version with Base arguments.<br>
<br>
Do we not have essentially all the same problems with classes, even with<br>
today&#39;s Equatable?  If not, what are the differences?
<br></blockquote><div> </div></div><div dir="ltr"><div class="gmail_quote"><div>We do have some problems today, such as above where using `==` on a `(Base, Subclass as Base)` pair ends up calling `==(Base, Base)` because we lack multiple dispatch. What surprised me though was that the `eq` call between two `Subclass` instances passed to the trampoline operator ended up calling `Base.eq`. I would have expected `Subclass.eq` to be called there since the generic argument `T` was bound to `Subclass`. Today, a non-generic `==(Subclass, Subclass)` operator *does* do the right thing.</div><div><br></div><div>I mainly called it out because the problems we have with `==` today are slightly different than the problems encountered when testing out the new model. If the decision is &quot;operators with Self constraints are fundamentally hard with classes and we don&#39;t necessarily expect them to work consistently&quot;, then that&#39;s fine, but I wanted to make sure it wasn&#39;t a hole in the proposal.</div><div><br></div></div></div></div>