> On May 27, 2016, at 5:07 PM, plx via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
>       
>> On May 27, 2016, at 3:22 PM, Ricardo Parada via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Inline
>> 
>> 
>> On May 27, 2016, at 2:52 PM, Matthew Johnson <matt...@anandabits.com 
>> <mailto:matt...@anandabits.com>> wrote:
>> 
>>> 
>>>> On May 27, 2016, at 12:48 PM, Ricardo Parada <rpar...@mac.com 
>>>> <mailto:rpar...@mac.com>> wrote:
>>>> 
>>>> 
>>>> 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?
>>> 
>>> Yes.  It would also be bad if implicit synthesis resulted in an 
>>> unintentional and incorrect definition of equality.  By requiring synthesis 
>>> to be requested with `deriving` the programmer is at least prompted to 
>>> consider the meaning of equality for their type.
>> 
>> Incorrect definition of equality? Hmm... :-)
>> 
>> I guess I have been running under the wrong assumption that if a struct uses 
>> values that are all Equatable then the default implementation for the struct 
>> which will compare the values against the values in the other struct will 
>> ALWAYS be correct. But I guess I can come up with an example where some of 
>> the values stored in the struct do not play a role in the definition of 
>> equality even if those values are Equatable. Then the default implementation 
>> would be incorrect. 
> 
> A recent one for me was a rational type, e.g. you’d want things like `1/2 == 
> 2/4` (and in this case I didn’t want an implementation that *would* always 
> automatically use fully-reduced internal representations).

It’s a bit of a tangent, but you might want to take a look at John Lakos’ 
discussion of rational equality in the talk Dave A posted a link to.  There are 
subtle differences in behavior (overflow, etc) between 1/2 and 2/4.  They are 
not actually substitutable when you have bounded integer types.  John makes 
some interesting observations on this topic.

> 
> I *do* think Swift is missing a “bit-by-bit/physical" equality operator (for 
> which 1/2 and 2/4 would be distinct, here), and Swift should probably get one 
> at some point, but that’s IMHO another (but related) discussion.
> 
>> But I am not convince that is bad because   that can happen regardless of 
>> whether equatable is an opt-in thing or automatic. For example, let's say 
>> you opt-in by saying that it implements Equatable or by using the derived / 
>> synthesizes keyword that we have mentioned. The developer may not realize 
>> until later that the default implementation would be wrong for your 
>> fancy/unusual struct.  It is likely that opting in may raise a flag in your 
>> brain that says "hey, is the default implementation going to do the right 
>> thing? Do you need to customize it for your struct?" But it's not a 
>> guarantee either. And if it's not a guarantee then should it be automatic 
>> then? Most developer will go with the default implementation when they 
>> opt-in and then realize later that they may need to customize when things 
>> are not working quite the way the expected. 
>> 
>> 
>>>> 
>>>> 
>>>>> On May 26, 2016, at 11:35 AM, Matthew Johnson via swift-evolution 
>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>> 
>>>>>> 
>>>>>> On May 26, 2016, at 10:18 AM, T.J. Usiyan via swift-evolution 
>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 
>>>>>> <swift-evolution@swift.org <mailto: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 <mailto: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 <mailto: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/ 
>>>>>> > <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 <mailto:swift-evolution@swift.org>
>>>>>> > https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>> >
>>>>>> >
>>>>>> > _______________________________________________
>>>>>> > swift-evolution mailing list
>>>>>> > swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>> > https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>> 
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <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