<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=""><div class="">I don’t understand why you couldn’t conform to Equatable, and if it fulfils the requirements (all members are also Equatable), then its implementation is automatically created for you. That way you don’t need a new keyword. It’s like Objective-C’s property automatic synthesising that get used unless you implement ones yourself.</div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On 27 May 2016, at 12:57 PM, Ricardo Parada via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="content-type" content="text/html; charset=utf-8" class=""><div dir="auto" class=""><div class=""><br class=""><br class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">I wonder if&nbsp;<b class="">synthesizes</b>&nbsp;would be a better choice than&nbsp;<b class="">deriving</b>.&nbsp;</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><br class="">On May 26, 2016, at 5:58 AM, Michael Peternell via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><span class="">Can we just copy&amp;paste the solution from Haskell instead of creating our own? It's just better in every aspect. Deriving `Equatable` and `Hashable` would become</span><br class=""><span class=""></span><br class=""><span class="">struct Polygon deriving Equatable, Hashable {</span><br class=""><span class=""> &nbsp;&nbsp;&nbsp;...</span><br class=""><span class="">}</span><br class=""><span class=""></span><br class=""><span class="">This has several advantages:</span><br class=""><span class="">- you don't have to guess wether `Equatable` or `Hashable` should be automatically derived or not.</span><br class=""><span class="">- Deriving becomes an explicit choice.</span><br class=""><span class="">- If you need a custom `Equatable` implementation (for whatever reason), you can still do it.</span><br class=""><span class="">- It doesn't break any code that is unaware of the change</span><br class=""><span class="">- It can be extended in future versions of Swift, without introducing any new incompatibilities. For example, `CustomStringConvertible` could be derived just as easily.</span><br class=""><span class="">- It is compatible with generics. E.g. `struct Shape&lt;T&gt; deriving Equatable` will make every `Shape&lt;X&gt;` equatable if `X` is equatable. But if `X` is not equatable, `Shape&lt;X&gt;` can be used as well. (Unless `X` is not used, in which case every `Shape&lt;T&gt;` would be equatable. Unless something in the definition of `Shape` makes deriving `Equatable` impossible =&gt; this produces an error.)</span><br class=""><span class="">- It is proven to work in production.</span><br class=""><span class=""></span><br class=""><span class="">-Michael</span><br class=""><span class=""></span><br class=""><blockquote type="cite" class=""><span class="">Am 26.05.2016 um 03:48 schrieb Mark Sands via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt;:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Thanks so much for putting this together, Tony! Glad I was able to be some inspiration. :^)</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">On Wed, May 25, 2016 at 1:28 PM, Tony Allevato via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">I was inspired to put together a draft proposal based on an older discussion in the Universal Equality, Hashability, and Comparability thread &lt;<a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/8919/" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/8919/</a>&gt; that recently got necromanced (thanks Mark Sands!).</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">I'm guessing that this would be a significant enough change that it's not possible for the Swift 3 timeline, but it's something that would benefit enough people that I want to make sure the discussion stays alive. If there are enough good feelings about it, I'll move it from my gist into an actual proposal PR.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Automatically deriving Equatable andHashable for value types</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• Proposal: SE-0000</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• Author(s): Tony Allevato</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• Status: Awaiting review</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• Review manager: TBD</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Introduction</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Value types are prevalent throughout the Swift language, and we encourage developers to think in those terms when writing their own types. Frequently, developers find themselves writing large amounts of boilerplate code to support equatability and hashability of value types. This proposal offers a way for the compiler to automatically derive conformance toEquatable and Hashable to reduce this boilerplate, in a subset of scenarios where generating the correct implementation is likely to be possible.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Swift-evolution thread: Universal Equatability, Hashability, and Comparability</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Motivation</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Building robust value types in Swift can involve writing significant boilerplate code to support concepts of hashability and equatability. Equality is pervasive across many value types, and for each one users must implement the == operator such that it performs a fairly rote memberwise equality test. As an example, an equality test for a struct looks fairly uninteresting:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">func ==(lhs: Foo, rhs: Foo) -&gt; Bool</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> {</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lhs.property1 == rhs.property1 &amp;&amp;</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lhs</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">.property2 == rhs.property2 &amp;&amp;</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lhs</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">.property3 == rhs.property3 &amp;&amp;</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">...</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">}</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">What's worse is that this operator must be updated if any properties are added, removed, or changed, and since it must be manually written, it's possible to get it wrong, either by omission or typographical error.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Likewise, hashability is necessary when one wishes to store a value type in a Set or use one as a multi-valuedDictionary key. Writing high-quality, well-distributed hash functions is not trivial so developers may not put a great deal of thought into them – especially as the number of properties increases – not realizing that their performance could potentially suffer as a result. And as with equality, writing it manually means there is the potential to get it wrong.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">In particular, the code that must be written to implement equality for enums is quite verbose. One such real-world example (source):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">func ==(lhs: HandRank, rhs: HandRank) -&gt; Bool</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> {</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">switch</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> (lhs, rhs) {</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.straightFlush(let lRank, let lSuit), .straightFlush(let rRank , let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rSuit)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lRank == rRank &amp;&amp; lSuit ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rSuit</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.fourOfAKind(four: let lFour), .fourOfAKind(four: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rFour)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lFour ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rFour</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.fullHouse(three: let lThree), .fullHouse(three: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rThree)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lThree ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rThree</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.flush(let lRank, let lSuit), .flush(let rRank, let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rSuit)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lSuit == rSuit &amp;&amp; lRank ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rRank</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.straight(high: let lRank), .straight(high: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rRank)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lRank ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rRank</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.threeOfAKind(three: let lRank), .threeOfAKind(three: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rRank)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lRank ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rRank</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.twoPair(high: let lHigh, low: let lLow, highCard: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> lCard),</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">.twoPair(high: let rHigh, low: let rLow, highCard: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lHigh == rHigh &amp;&amp; lLow == rLow &amp;&amp; lCard ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.onePair(let lPairRank, card1: let lCard1, card2: let lCard2, card3: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> lCard3),</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">.onePair(let rPairRank, card1: let rCard1, card2: let rCard2, card3: let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard3)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lPairRank == rPairRank &amp;&amp; lCard1 == rCard1 &amp;&amp; lCard2 == rCard2 &amp;&amp; lCard3 ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard3</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case (.highCard(let lCard), .highCard(let</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard)):</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return lCard ==</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> rCard</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">default</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">return false</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp;}</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">}</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Crafting a high-quality hash function for this enum would be similarly inconvenient to write, involving another large switchstatement.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Swift already provides implicit protocol conformance in some cases; notably, enums with raw values conform toRawRepresentable, Equatable, and Hashable without the user explicitly declaring them:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">enum Foo: Int</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> {</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case one = 1</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">case two = 2</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">}</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">let x = (Foo.one == Foo.two) &nbsp;// works</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">let y = Foo.one.hashValue &nbsp;&nbsp;&nbsp;&nbsp;// also works</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">let z = Foo.one.rawValue &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// also also works</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Since there is precedent for this in Swift, we propose extending this support to more value types.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Proposed solution</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">We propose that a value type be Equatable/Hashable if all of its members are Equatable/Hashable, with the result for the outer type being composed from its members.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Specifically, we propose the following rules for deriving Equatable:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• A struct implicitly conforms to Equatable if all of its fields are of types that conform to Equatable – either explicitly, or implicitly by the application of these rules. The compiler will generate an implementation of ==(lhs: T, rhs: T)that returns true if and only if lhs.x == rhs.x for all fields x in T.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• An enum implicitly conforms to Equatable if all of its associated values across all of its cases are of types that conform to Equatable – either explicitly, or implicitly by the application of these rules. The compiler will generate an implementation of ==(lhs: T, rhs: T) that returns true if and only if lhs and rhs are the same case and have payloads that are memberwise-equal.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Likewise, we propose the following rules for deriving Hashable:</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• A struct implicitly conforms to Hashable if all of its fields are of types that conform to Hashable – either explicitly, or implicitly by the application of these rules. The compiler will generate an implementation of hashValue that uses a pre-defined hash function† to compute the hash value of the struct from the hash values of its members.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Since order of the terms affects the hash value computation, we recommend ordering the terms in member definition order.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""> &nbsp; &nbsp;• An enum implicitly conforms to Hashable if all of its associated values across all of its cases are of types that conform to Hashable – either explicitly, or implicitly by the application of these rules. The compiler will generate an implementation of hashValue that uses a pre-defined hash function† to compute the hash value of an enum value by using the case's ordinal (i.e., definition order) followed by the hash values of its associated values as its terms, also in definition order.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">† We leave the exact definition of the hash function unspecified here; a multiplicative hash function such as Kernighan and Ritchie or Bernstein is easy to implement, but we do not rule out other possibilities.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Overriding defaults</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Any user-provided implementations of == or hashValue should override the default implementations that would be provided by the compiler. This is already possible today with raw-value enums so the same behavior should be extended to other value types that are made to implicitly conform to these protocols.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Open questions</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Omission of fields from generated computations</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Should it be possible to easily omit certain properties from automatically generated equality tests or hash value computation? This could be valuable, for example, if a property is merely used as an internal cache and does not actually contribute to the "value" of the instance. Under the rules above, if this cached value was equatable, a user would have to override == and hashValue and provide their own implementations to ignore it. If there is significant evidence that this pattern is common and useful, we could consider adding a custom attribute, such as @transient, that would omit the property from the generated computations.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Explicit or implicit derivation</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">As with raw-value enums today, should the derived conformance be completely explicit, or should users have to explicitly list conformance with Equatable and Hashable in order for the compiler to generate the derived implementation?</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Impact on existing code</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">This change will have no impact on existing code because it is purely additive. Value types that already provide custom implementations of == or hashValue but satisfy the rules above would keep the custom implementation because it would override the compiler-provided default.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Alternatives considered</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">The original discussion thread also included Comparable as a candidate for automatic generation. Unlike equatability and hashability, however, comparability requires an ordering among the members being compared. Automatically using the definition order here might be too surprising for users, but worse, it also means that reordering properties in the source code changes the code's behavior at runtime. (This is true for hashability as well if a multiplicative hash function is used, but hash values are not intended to be persistent and reordering the terms does not produce a significant behavioral change.)</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Acknowledgments</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">Thanks to Joe Groff for spinning off the original discussion thread, Jose Cheyo Jimenez for providing great real-world examples of boilerplate needed to support equatability for some value types, and to Mark Sands for necromancing the swift-evolution thread that convinced me to write this up.</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">_______________________________________________</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">swift-evolution mailing list</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""></span><br class=""></blockquote><blockquote type="cite" class=""><span class="">_______________________________________________</span><br class=""></blockquote><blockquote type="cite" class=""><span class="">swift-evolution mailing list</span><br class=""></blockquote><blockquote type="cite" class=""><span class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a></span><br class=""></blockquote><blockquote type="cite" class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br class=""></blockquote><span class=""></span><br class=""><span class="">_______________________________________________</span><br class=""><span class="">swift-evolution mailing list</span><br class=""><span class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br class=""></div></blockquote></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>