Sent from my iPad

> On May 29, 2016, at 9:12 AM, 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.
> 
> 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

The same members should be considered by both Equatable and Hashable so there 
would be no need for two separate annotations.  A more general annotation 
(maybe "nonessential"?) is what we want.

> 
> 
>> 
>> 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

Reply via email to