Sent from my iPad

> On May 27, 2016, at 10:08 PM, Patrick Smith via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> A different way of layering could be allowing value types to be composed, 
> where the outer type inherits the inner member’s properties and methods.
> 
> Let’s say you want only fields ‘contentID’ and ‘contentData' to participate 
> in equality and hashing, but not ‘revisionID':
> 
>   struct ContentInfo : Equatable, Hashable { // Automatic implementations for 
> == and hashValue are provided since members conform
>     let contentID: NSUUID
>     let contentData: NSData
>   }
> 
>   struct RevisionInfo : Equatable, Hashable {
>     let revisionID: NSUUID
>     private let content: ContentInfo // Hidden from the outside world
>     public compose content // Adds .contentID, .contentData, .hashValue 
> properties to RevisionInfo that delegate to those of `content`
>   }
> 
>   func ==(lhs: RevisionInfo, rhs: RevisionInfo) -> Bool {
>     return lhs.content == rhs.content
>   }

This is pretty similar to embedding in Go.  It is an interesting feature but I 
don't think it's that relevant to compiler synthesized Equatable and Hashable. 

> 
> 
>>> On 28 May 2016, at 5:41 AM, plx via swift-evolution 
>>> <swift-evolution@swift.org> wrote:
>>> 
>>> 
>>>> 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> wrote:
>>>>> 
>>>>> 
>>>> 
>>>>> On May 26, 2016, at 1:00 PM, T.J. Usiyan via swift-evolution 
>>>>> <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
> 
> _______________________________________________
> 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