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