<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On May 5, 2017, at 2:16 PM, Tony Allevato &lt;<a href="mailto:tony.allevato@gmail.com" class="">tony.allevato@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" 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; -webkit-text-stroke-width: 0px;" class=""><div class="gmail_quote"><div dir="ltr" class="">On Fri, May 5, 2017 at 11:51 AM Matthew Johnson &lt;<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.com</a>&gt; wrote:<br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><blockquote type="cite" class=""><div class="">On May 5, 2017, at 1:33 PM, Tony Allevato &lt;<a href="mailto:tony.allevato@gmail.com" target="_blank" class="">tony.allevato@gmail.com</a>&gt; wrote:</div><br class="m_-4013634017692843344Apple-interchange-newline"><div class=""><div dir="ltr" class=""><br class=""><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Fri, May 5, 2017 at 11:07 AM Matthew Johnson &lt;<a href="mailto:matthew@anandabits.com" target="_blank" class="">matthew@anandabits.com</a>&gt; wrote:<br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><blockquote type="cite" class=""><div class="">On May 5, 2017, at 10:45 AM, Tony Allevato via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_-4013634017692843344m_4844033761213030745Apple-interchange-newline"><div class=""><div dir="ltr" class="">Thanks for your feedback, everybody!</div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class="">Thanks for continuing to drive this forward!</div><div class=""></div></div><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">I've updated the<span class="Apple-converted-space">&nbsp;</span><a href="https://gist.github.com/allevato/2fd10290bfa84accfbe977d8ac07daad" target="_blank" class="">gist</a>&nbsp;to reflect what seems to be a consensus here:</div><div class=""><br class=""></div><div class="">* Derived conformances are now opt-in (this makes the recursive case *much* cleaner, and the complexity involved in that section has been completely removed)</div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">Can the opt-in conformance be declared in an extension?&nbsp; If so, can the extension be in a different module than the original declaration?&nbsp; If so, do you intend any restrictions, such as requiring all members of the type declared in a different module to be public?&nbsp; My initial thought is that this should be possible as long as all members are visible.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Declaring the conformance in an extension in the same module should definitely be allowed; I believe this would currently be the only way to support conditional conformances (such as the `Optional: Hashable where Wrapped: Hashable` example in the updated draft), without requiring deeper syntactic changes.</div><div class=""><br class=""></div><div class="">I'm less sure about conformances being added in other modules, but I'm inclined to agree with your assessment. I could see two ways of interpreting it:</div><div class=""><br class=""></div><div class="">* E/H can only be derived in an extension in an external module if all the members are accessible (and the other conditions are met).</div><div class="">* E/H can be derived in an extension in an external module using only the subset of accessible members (if the other conditions are met).</div><div class=""><br class=""></div><div class="">These are subtly different. The argument for the first would be "if you want to add E/H to a type in a different module, you must *consciously* decide which members you want to use in those computations". The argument for the second would be "you can already make a type in a different module conform to E/H and you'd be restricted to the accessible members there, so let's make that path easier for users too."</div><div class=""><br class=""></div><div class="">The first case is probably the safer choice. I'm not sure about the implementation difficulty of each.</div><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="">* Classes are supported now as well</div><div class=""><br class=""></div><div class="">Please take a look at the updated version and let me know if there are any concerns! If folks like it, I'll prepare a pull request.</div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">Will the synthesis for classes dispatch through a non-final method which is expected to be overridden by subclasses?&nbsp; You don’t explicitly state this but it seems implied.&nbsp; If so, what if &nbsp;the subclass requires a custom implementation?&nbsp; This would require the signature of the non-final method to be part of the synthesis contract.</div></div></div></blockquote><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><div class=""><br class=""></div><div class="">Supporting non-final classes introduces enough complexity (especially when multiple modules are involved).&nbsp; I would hate to see it get sidetracked in discussions regarding non-final classes and miss the Swift 4 window because of that.&nbsp; Given the limited time left for Swift 4 it might be better to keep the initial proposal simpler and consider a followup in the Swift 5 timeframe to build on the initial proposal.</div></div></div></blockquote><div class=""><br class=""></div><div class="">For ==, the operator must already be "class final" or "static" regardless of this proposal, and it can't be "overridden" as such in subclasses because the arguments would be different (lhs and rhs would be the subclass, not the superclass). So the compiler should be able to generate the correct implementation for subclasses in all cases, right?</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class="">This won’t work because Equatable has a `Self` requirement so the `==` defined by the initial conforming class would be called.&nbsp; In order to support non-final classes you would need to have that dispatch through something like an `isEqual` method which *can* be overridden.</div></div></blockquote><div class=""><br class=""></div><div class="">Ah crap, you're right. I was basing my experimentation on this example:</div><div class=""><br class=""></div><div class=""><pre style="word-wrap: break-word; white-space: pre-wrap;" class="">class Base: Equatable {
  let x: Int
  init(x: Int) { self.x = x }
  static func == (lhs: Base, rhs: Base) -&gt; Bool { return lhs.x == rhs.x }
}

class Sub: Base {
  let y: Int
  init(x: Int, y: Int) { self.y = y; super.init(x: x) }

  static func == (lhs: Sub, rhs: Sub) -&gt; Bool {
    guard lhs as Base == rhs as Base else {
      return false
    }
    return lhs.y == rhs.y
  }
}

let s1 = Sub(x: 1, y: 2)
let s2 = Sub(x: 1, y: 3)
let s3 = Sub(x: 1, y: 2)

func usesBase(_ lhs: Base, _ rhs: Base) -&gt; Bool { return lhs == rhs }
func usesSub(_ lhs: Sub, _ rhs: Sub) -&gt; Bool { return lhs == rhs }

print(usesSub(s1, s2)) // false (expected)
print(usesSub(s1, s3)) // true

print(usesBase(s1, s2)) // true (problematic)
print(usesBase(s1, s3)) // true
</pre><br class="inbox-inbox-Apple-interchange-newline"></div><div class="">In that case the problematic behavior is "expected", but when you make it generic, you're right that you still get the problematic behavior which kind of makes the conformance useless:</div><div class=""><br class=""></div><div class=""><pre style="word-wrap: break-word; white-space: pre-wrap;" class="">func usesGeneric&lt;T: Equatable&gt;(_ lhs: T, _ rhs: T) -&gt; Bool {
  return lhs == rhs
}

print(usesGeneric(s1, s2)) // true (problematic)
print(usesGeneric(s1, s3)) // true</pre></div><div class=""><br class=""></div><div class="">Sadly, I don't work with classes/inheritance enough in Swift to run into these problems on a regular basis.</div><div class=""><br class=""></div><div class="">FWIW, the same issue came up in&nbsp;<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0091-improving-operators-in-protocols.md" class="">SE-0091</a>&nbsp;and the consensus seemed to be that since Equatable already didn't work the "right way" for inheritance anyway, that would need to be fixed separately, so I'd be interested to know if the core team feels the same way here.</div><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""></div></div><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">For hashValue, I think the possibilities are:</div><div class=""><br class=""></div><div class="">* Sub is a subclass of Super. Super conforms to Hashable and implements non-final hashValue. The compiler can derive it for Sub and call super.hashValue in its implementation.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">Yes, this makes sense.&nbsp; The primary difficulty with Hashable is that it refines Equatable.&nbsp; Refining a non-final implementation of `hashValue` is relatively straightforward.</div></div></div></blockquote><div class=""><br class=""></div><div class="">Right... there could be situations where a function that takes a generic &lt;T: Hashable&gt; gets inconsistent results for == and hashValue because the latter is dynamically dispatched but the former isn't. That's already the situation we have today, but it could be argued that deriving implementations would "make the bleeding worse".</div><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class="">* Sub is a subclass of Super. Super conforms to Hashable and implements a final hashValue. The compiler cannot derive one for Super and would silently not do so.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">Do you mean “the compiler cannot derive one for Sub”?</div></div></div></blockquote><div class=""><br class=""></div><div class="">Yes, sorry.</div><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class="">* Sub is a subclass of Super. Super does not conform to Hashable, but Sub asks to derive it. This can either (1) not be allowed, telling the user that they need to write it manually in this case, or (2) be allowed and use all accessible members to compute the hashValue (including those from the superclass).</div><div class=""><br class=""></div><div class="">What do Encodable/Decodable do in these situations? It seems similar solutions there would apply here.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">That’s a good question.&nbsp; I don’t recall whether this was addressed explicitly or not.</div></div></div><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">But after writing this all out, I'm inclined to agree that I'd rather see structs/enums make it into Swift 4 even if it meant pushing classes to Swift 4+x.</div></div></div></div></blockquote><div class=""><br class=""></div></div></div><div style="word-wrap: break-word;" class=""><div class=""><div class="">That is reasonable. &nbsp;</div><div class=""><br class=""></div><div class="">On the other hand, I think you could come up with straightforward semantics for synthesizing conformance for final classes as well.&nbsp; Final classes with no superclass should be straightforward.&nbsp; Final classes that do have a superclass would be similarly straightforward if we decide to allow this as described in option (2) above regarding hashValue. &nbsp;</div><div class=""><br class=""></div><div class="">I’m on the fence on this - if we can include final classes using option (2) without jeopardizing getting this in for Swift 4 I would support that.&nbsp; If it’s going to put support for value types in Swift 4 at risk then I would not.</div></div></div></blockquote><div class=""><br class=""></div><div class="">I'm on the fence as well. It sounds like we could scratch non-final classes off the list for now without losing much because they're already very problematic. The question then is, how much worse does supporting final classes make the implementation.</div><div class=""><br class=""></div><div class="">Since I only did the work for enums in my initial experiment, I don't have enough insight yet to know for sure. Hopefully I'll have some time in the next couple days to try it out.</div></div></div></div></blockquote><div><br class=""></div><div>Sounds good. &nbsp;Since you’re working on the implementation I’m happy deferring to your judgment. &nbsp;:)</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" 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; -webkit-text-stroke-width: 0px;" class=""><div class="gmail_quote"><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_quote"><div class=""><br class=""></div><div class="">&nbsp;</div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""><br class=""><div class="gmail_quote"><div dir="ltr" class="">On Fri, May 5, 2017 at 8:16 AM Nevin Brackett-Rozinsky via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote">On Fri, May 5, 2017 at 1:47 AM, Xiaodi Wu via swift-evolution<span class="Apple-converted-space">&nbsp;</span><span dir="ltr" class="">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt;</span><span class="Apple-converted-space">&nbsp;</span>wrote:<br class=""></div></div></div><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="ltr" class=""><span class="m_-4013634017692843344m_4844033761213030745m_1203078577276602968gmail-">On Fri, May 5, 2017 at 12:41 AM, Brent Royal-Gordon<span class="Apple-converted-space">&nbsp;</span><span dir="ltr" class="">&lt;<a href="mailto:brent@architechies.com" target="_blank" class="">brent@architechies.com</a>&gt;</span><span class="Apple-converted-space">&nbsp;</span>wrote:</span><div class="gmail_extra"><div class="gmail_quote"><span class="m_-4013634017692843344m_4844033761213030745m_1203078577276602968gmail-"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class="">I would think only final classes could participate in this, since a subclassable class would need to allow subclasses to override equality, and you can't override a static `==` operator method.</div></div></blockquote><div class=""><br class=""></div></span><div class="">I work so rarely with classes that I'm embarrassed to have to ask this question: can classes not satisfy Equatable with a `public class func ==`?</div></div></div></div></blockquote></div></div></div><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="ltr" class=""></div></blockquote><div class=""><br class=""></div><div class="">Currently:</div><div class=""><br class=""></div><div class=""><div class=""><font face="monospace, monospace" class="">class C: Equatable {</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span>class func == (lhs: C, rhs: C) -&gt; Bool {</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp; &nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span>return lhs === rhs</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span>}</font></div><div class=""><font face="monospace, monospace" class="">}</font></div></div><div class=""><br class=""></div><div class="">Yields an error, “Operator '==' declared in non-final class 'C' must be 'final'”.</div></div></div></div><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><div class=""><br class=""></div><div class="">Nevin</div></div></div></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></blockquote></div></div></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote></div></div></blockquote></div></div></div></blockquote></div></div></blockquote></div></div></div></blockquote></div><br class=""></body></html>