I agree, I think there is no need for deriving/synthesizes, but also, no need to specify Equatable, Hashable. They should get the functionality by default but allow for override and customization.
> On May 26, 2016, at 11:02 PM, Patrick Smith <pgwsm...@gmail.com> wrote: > > 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. > > >> On 27 May 2016, at 12:57 PM, Ricardo Parada via swift-evolution >> <swift-evolution@swift.org> wrote: >> >> >> >> 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 >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution