> On May 29, 2016, at 4:12 PM, Vladimir.S via swift-evolution
> <[email protected]> wrote:
>
> On 27.05.2016 18:37, plx via swift-evolution wrote:
>>
>>> On May 26, 2016, at 1:00 PM, T.J. Usiyan via swift-evolution
>>> <[email protected] <mailto:[email protected]>> wrote:
>>>
>>> A `deriving` keyword, at the very least, is pretty explicitly *not* an
>>> all-or-nothing situation. If you want to define equality/hashability for
>>> your type manually, don't use `deriving`. This should leave the simplest
>>> cases to auto generation and anything more complex should be handled by
>>> the developer.
>>
>> It’s all-or-nothing in the sense you can’t use a naive `deriving`
>> implementation to assist in any case where what you need is *almost* the
>> trivial implementation, but not quite.
>
> I support that we need a way to exclude some fields from participate in
> auto-derived code. It is not handy if we'll have just one-two excluded
> properties (of 10 for example) and we'll need to implement the boilerplate
> code because of this. Probably, this should be another proposal for this
> feature.
>
the fact that other existing systems work that way for years is a good
indication that it is a REAL need.
> Just some thoughts: such a method to decorate some fields as
> `nonequatable`/`nonhashable` has no meaning *if* Equatable/Hashable is later
> implemented manually. So, to remove confusion, it seems like such 'method' to
> exclude should be disallowed(by compiler) if protocols are implemented
> manually by coder.
> I.e., I don't want to see a code where we have *both* explicit
> implementations of protocols *and* some decorators/special functions to
> exclude some fields as this will no any sense.
>
> Also for me it seems like we need to be able to define such attribute near
> the field itself, to prevent later errors when you define new field but
> forgot to add it to some 'special list' of excluded field somewhere in code.
> So, probably I'd prefer some kind of @nonequatable and @nonhashable :
> @nonequatable var field = 0
>
>
>>
>> Consider a case like this:
>>
>> class QuxEvaluator {
>>
>> let foo: Foo // Equatable
>> let bar: Bar // Equatable
>> let baz: Baz // Equatable
>>
>> private var quxCache: [QuxIdentifier:Qux] // [Equatable:Equatable] = [:]
>>
>> // pure function of `foo`, `bar`, `baz`, and `identifier`
>> // expensive, and uses `quxCache` for memoization
>> func qux(for identifier: QuxIdentifier) -> Qux
>>
>> }
>>
>> …if it weren’t for `quxCache` we could easily synthesize `==` for
>> `QuxEvaluator`, but the trivial synthesis will yield invalid results due to
>> `[QuxIdentifier:Qux]` also being `Equatable` (really: it *will* also be
>> equatable once conditional conformances are in place).
>>
>> So we’re back to e.g. writing this:
>>
>> extension QuxEvaluator : Equatable {
>>
>> }
>>
>> func ==(lhs: QuxEvaluator, rhs: QuxEvaluator) -> Bool {
>> return (lhs === rhs) || (lhs.foo == rhs.foo && lhs.bar == rhs.bar &&
>> lhs.baz == rhs.baz)
>> }
>>
>> …just to omit a single field from the `==` consideration; this is another
>> sense in which you can say deriving is an all-or-none; there’s just no way
>> to invoke the synthesis mechanism other than for "all fields”.
>>
>> On the one hand, it’s possible to imagine a finer-grained form of this
>> synthesis that’d allow you to e.g. indicate a certain field should be
>> omitted (and also perhaps specialize how fields are compared, customize the
>> synthesized comparison ordering to put cheaper comparisons earlier, and an
>> endless list of other possible requests…).
>>
>> On the other hand, the memberwise-init proposal had a very similar
>> situation: the naive approach was arguably too narrow, but it proved very
>> difficult to find a workable balance between customization and
>> implementation complexity (leaving it postponed for the foreseeable
>> future); it’d be a pity if synthesis like this fell into the same trap.
>>
>> But, on the gripping hand, I highly suspect that a naive
>> `deriving`/synthesis will wind up being too narrowly-useful to really
>> justify; that’s just my opinion, of course.
>>
>>>
>>> On Thu, May 26, 2016 at 11:20 AM, L. Mihalkovic
>>> <[email protected] <mailto:[email protected]>> wrote:
>>>
>>> what i care about is to have a choice about what DEFINES the identity
>>> of my values, not just an all-or-nothing situation.
>>>
>>> On May 26, 2016, at 5:18 PM, T.J. Usiyan via swift-evolution
>>> <[email protected] <mailto:[email protected]>> wrote:
>>>
>>>> +1 to a `deriving` keyword
>>>>
>>>> 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/>
>>>> 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
>>>> >
>>>> >
>>>> > _______________________________________________
>>>> > 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
>>>>
>>>>
>>>> _______________________________________________
>>>> 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
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution