When would the automatic implementation fail? I see your point about adding to the compile size and I had previously mentioned that. Usually in the apps I develop that is not a concern. However I can see it could be a concern for apps that are more restrictive. If Swift is to be used to build such apps too then I agree that not being automatic would be important. It would have to be opt-in. I would be okay with that solution. Hopefully most would make use of it in high-level frameworks.
I guess similar to reflection and enabling dynamism. I look forward to eventually being able to do key-value-coding in pure Swift and being able to implement a framework similar to WebObjects which would require being able to find properties by their name, getting and setting values. That would be another example of opting in for a richer run-time. > On May 27, 2016, at 10:11 AM, Patrick Smith <pgwsm...@gmail.com> wrote: > > If you have to specify Equatable / Hashable, then you can get an error if the > automatic implementation failed, due to a member also not being > Equatable/Hashable. If it’s automatic, then it will just quietly fail, making > problems harder to know about and track. It will also always add to the > compile size, even if you didn’t want the functionality. > > >> On 27 May 2016, at 10:46 PM, Ricardo Parada <rpar...@mac.com> wrote: >> >> 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