> On May 27, 2016, at 12:48 PM, Ricardo Parada <[email protected]> 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.

> 
> 
>> On May 26, 2016, at 11:35 AM, Matthew Johnson via swift-evolution 
>> <[email protected] <mailto:[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 
>>> <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

Reply via email to