I wonder if synthesizes would be a better choice than deriving. 



> On May 26, 2016, at 5:58 AM, Michael Peternell via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Can we just copy&paste the solution from Haskell instead of creating our own? 
> It's just better in every aspect. Deriving `Equatable` and `Hashable` would 
> become
> 
> struct Polygon deriving Equatable, Hashable {
>    ...
> }
> 
> This has several advantages:
> - you don't have to guess wether `Equatable` or `Hashable` should be 
> automatically derived or not.
> - Deriving becomes an explicit choice.
> - If you need a custom `Equatable` implementation (for whatever reason), you 
> can still do it.
> - It doesn't break any code that is unaware of the change
> - It can be extended in future versions of Swift, without introducing any new 
> incompatibilities. For example, `CustomStringConvertible` could be derived 
> just as easily.
> - It is compatible with generics. E.g. `struct Shape<T> deriving Equatable` 
> will make every `Shape<X>` equatable if `X` is equatable. But if `X` is not 
> equatable, `Shape<X>` can be used as well. (Unless `X` is not used, in which 
> case every `Shape<T>` would be equatable. Unless something in the definition 
> of `Shape` makes deriving `Equatable` impossible => this produces an error.)
> - It is proven to work in production.
> 
> -Michael
> 
>> Am 26.05.2016 um 03:48 schrieb Mark Sands via swift-evolution 
>> <swift-evolution@swift.org>:
>> 
>> Thanks so much for putting this together, Tony! Glad I was able to be some 
>> inspiration. :^)
>> 
>> 
>> On Wed, May 25, 2016 at 1:28 PM, Tony Allevato via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> I was inspired to put together a draft proposal based on an older discussion 
>> in the Universal Equality, Hashability, and Comparability thread 
>> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8919/> that 
>> recently got necromanced (thanks Mark Sands!).
>> 
>> 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.
>> 
>> Automatically deriving Equatable andHashable for value types
>> 
>>    • Proposal: SE-0000
>>    • Author(s): Tony Allevato
>>    • Status: Awaiting review
>>    • Review manager: TBD
>> Introduction
>> 
>> 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.
>> 
>> Swift-evolution thread: Universal Equatability, Hashability, and 
>> Comparability
>> 
>> Motivation
>> 
>> 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:
>> 
>> func ==(lhs: Foo, rhs: Foo) -> Bool
>> {
>> 
>> return lhs.property1 == rhs.property1 &&
>> 
>>         lhs
>> .property2 == rhs.property2 &&
>> 
>>         lhs
>> .property3 == rhs.property3 &&
>> 
>> 
>> ...
>> 
>> }
>> 
>> 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.
>> 
>> 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.
>> 
>> In particular, the code that must be written to implement equality for enums 
>> is quite verbose. One such real-world example (source):
>> 
>> func ==(lhs: HandRank, rhs: HandRank) -> Bool
>> {
>> 
>> switch
>> (lhs, rhs) {
>> 
>> case (.straightFlush(let lRank, let lSuit), .straightFlush(let rRank , let
>> rSuit)):
>> 
>> return lRank == rRank && lSuit ==
>> rSuit
>> 
>> case (.fourOfAKind(four: let lFour), .fourOfAKind(four: let
>> rFour)):
>> 
>> return lFour ==
>> rFour
>> 
>> case (.fullHouse(three: let lThree), .fullHouse(three: let
>> rThree)):
>> 
>> return lThree ==
>> rThree
>> 
>> case (.flush(let lRank, let lSuit), .flush(let rRank, let
>> rSuit)):
>> 
>> return lSuit == rSuit && lRank ==
>> rRank
>> 
>> case (.straight(high: let lRank), .straight(high: let
>> rRank)):
>> 
>> return lRank ==
>> rRank
>> 
>> case (.threeOfAKind(three: let lRank), .threeOfAKind(three: let
>> rRank)):
>> 
>> return lRank ==
>> rRank
>> 
>> case (.twoPair(high: let lHigh, low: let lLow, highCard: let
>> lCard),
>> 
>> .twoPair(high: let rHigh, low: let rLow, highCard: let
>> rCard)):
>> 
>> return lHigh == rHigh && lLow == rLow && lCard ==
>> rCard
>> 
>> case (.onePair(let lPairRank, card1: let lCard1, card2: let lCard2, card3: 
>> let
>> lCard3),
>> 
>> .onePair(let rPairRank, card1: let rCard1, card2: let rCard2, card3: let
>> rCard3)):
>> 
>> return lPairRank == rPairRank && lCard1 == rCard1 && lCard2 == rCard2 && 
>> lCard3 ==
>> rCard3
>> 
>> case (.highCard(let lCard), .highCard(let
>> rCard)):
>> 
>> return lCard ==
>> rCard
>> 
>> default
>> :
>> 
>> return false
>> 
>>  }
>> }
>> 
>> Crafting a high-quality hash function for this enum would be similarly 
>> inconvenient to write, involving another large switchstatement.
>> 
>> 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:
>> 
>> enum Foo: Int
>> {
>> 
>> case one = 1
>> 
>> 
>> case two = 2
>> 
>> }
>> 
>> 
>> let x = (Foo.one == Foo.two)  // works
>> let y = Foo.one.hashValue     // also works
>> let z = Foo.one.rawValue      // also also works
>> Since there is precedent for this in Swift, we propose extending this 
>> support to more value types.
>> 
>> Proposed solution
>> 
>> 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.
>> 
>> Specifically, we propose the following rules for deriving Equatable:
>> 
>>    • 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.
>> 
>>    • 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.
>> 
>> Likewise, we propose the following rules for deriving Hashable:
>> 
>>    • 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.
>> 
>> Since order of the terms affects the hash value computation, we recommend 
>> ordering the terms in member definition order.
>> 
>>    • 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.
>> 
>> † 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.
>> 
>> Overriding defaults
>> 
>> 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.
>> 
>> Open questions
>> 
>> Omission of fields from generated computations
>> 
>> 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.
>> 
>> Explicit or implicit derivation
>> 
>> 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?
>> 
>> Impact on existing code
>> 
>> 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.
>> 
>> Alternatives considered
>> 
>> 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.)
>> 
>> Acknowledgments
>> 
>> 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.
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to