What if we get the error when trying to use it? For example, if a struct uses a value that is not Equatable / Hashable then it would not be Equatable / Hashable and you would not find out until you tried to use it. Would that be bad?
> On May 26, 2016, at 11:35 AM, Matthew Johnson via swift-evolution > <[email protected]> wrote: > >> >> On May 26, 2016, at 10:18 AM, T.J. Usiyan via swift-evolution >> <[email protected] <mailto:[email protected]>> wrote: >> >> +1 to a `deriving` keyword > > + 1. I like it as well. It makes the feature opt-in, declaring conformance > and requesting synthesis at the same time. The syntactic difference from a > simple conformance declaration means manual conformance can still be checked > properly with no ambiguity about whether you were requesting synthesis or > not. This approach also generalizes well. > > This bullet makes me uncomfortable though: > >> - 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. > > > You should not be able to just say `struct Shape<T> deriving Equatable`. You > should have to do this: > > extension Shape deriving Equatable where T: Equatable {} > > Or some equivalent syntax that makes it clear that you only intend to derive > equatable when T meets the stated conditions. > >> >> On Thu, May 26, 2016 at 3:58 AM, Michael Peternell via swift-evolution >> <[email protected] <mailto:[email protected]>> 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 >> > <[email protected] <mailto:[email protected]>>: >> > >> > 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 >> > <[email protected] <mailto:[email protected]>> 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/ >> > <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 >> > [email protected] <mailto:[email protected]> >> > https://lists.swift.org/mailman/listinfo/swift-evolution >> > <https://lists.swift.org/mailman/listinfo/swift-evolution> >> > >> > >> > _______________________________________________ >> > swift-evolution mailing list >> > [email protected] <mailto:[email protected]> >> > https://lists.swift.org/mailman/listinfo/swift-evolution >> > <https://lists.swift.org/mailman/listinfo/swift-evolution> >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution > > _______________________________________________ > swift-evolution mailing list > [email protected] <mailto:[email protected]> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
