> 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

Reply via email to