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

Reply via email to