> On May 27, 2016, at 10:48 AM, Matthew Johnson <matt...@anandabits.com> wrote:
>
>>
>> On May 27, 2016, at 10:37 AM, plx via swift-evolution
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>>
>>> On May 26, 2016, at 1:00 PM, T.J. Usiyan via swift-evolution
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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.
>>
>> 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”.
>
> I don’t see why this must necessarily be the case. Annotations such as you
> describe below could be taken into account by `deriving`. `deriving` is just
> a way to invoke the synthesis mechanism.
Different people are using it differently I think; I agree with you if it’s
just the name of the invocation, but I think at least some people are using it
as a shorthand for the “naive” implementation (all fields equatable =>
equatable).
That is, I meant "naive deriving” to refer to something like this (quoting
Patrick):
> It would fail if not all members were Equatable or Hashable. If it was
> automatic, you wouldn’t get any warning or sign at all. If you have to
> explicitly conform to the protocols, then your intention is clear, and if an
> automatic implementation cannot be made (because not all members were
> Equatable or Hashable), then you will get an error that you need to implement
> the protocol yourself like you do now (i.e. implement == and hashValue).
…but I could’ve been clearer!
>
>>
>> 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…).
>
> If you don’t trust the compiler to optimize this well and therefore want
> control over order of comparisons you should probably just implement it
> manually. As you note below, this is a convenience feature that needs to
> strike a fine balance.
I agree, but at the same time i think that scenarios like this:
struct RevisionInfo {
let contentID: NSUUID
let revisionID: NSUUID
let contentData: NSData
}
…aren’t going to be all that uncommon in practice; I think a good “layered”
implementation of the derivation/synthesis logic would suffice (e.g. we
wouldn't *need* special-case handling for ordering, potentially…).
>
> IMO there are two issues involved:
>
> 1. How do we invoke the automatic synthesis.
> 2. How do we have some degree of control over the synthesis that happens.
>
> `deriving` addresses issue 1 and says nothing about issue 2.
Agreed here; 2 is the interesting question. If you look at my initial response
in this thread I tried to suggest a “layered” approach:
Layer A: have some way of directly invoking the synthesis mechanism itself
(e.g. as a special-purpose macro-like construct); it should be powerful enough
to make `==` easy to write, but have some flexibility (implemented or
planned-for-future).
Layer B: add a way to synthesize `==` (etc.) via the construct from Layer A.
That’s my 2c on this topic; given it’s a Swift 4 topic at the very earliest
there’s a lot of time to figure it out.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution