<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">The problem addressed is real but I have a few reservations about the proposal as-sketched.</div><div class=""><br class=""></div><div class="">My first reservation is that, as-described, this would seem very likely to have some very bizarre and unintuitive cross-interactions if implemented alongside any construct along these lines:</div><div class=""><br class=""></div><div class="">// extension conforming `Foo` to `Bar` whenever $Condition is met</div><div class="">extension Foo: Bar where $Condition [</div><div class=""><br class=""></div><div class="">}</div><div class=""><br class=""></div><div class="">…due to ambiguities wherein, say, a base class explicitly conforms to some protocols, and consequently obtains an additional conformance via some conditional extension; a subclass explicitly re-conforms to the same protocols the base explicitly conforms-to…should it pick up that same “additional” conformance or not?</div><div class=""><br class=""></div><div class="">My second reservation is that, as I understand the proposal, it seems you could easily create protocol constraints for which no useful type would “naturally” work; e.g. as I understand it, if I happened to have this:</div><div class=""><br class=""></div><div class="">class Whatever<</div><div class=""> K:SomeBaseClass</div><div class=""> where</div><div class=""> K:ProtocolA,</div><div class=""> K:ProtocolB,</div><div class=""> K:ProtocolC> </div><div class=""><br class=""></div><div class="">…then to be able to actually write this:</div><div class=""><br class=""></div><div class="">let whatever = Whatever<SomeClass>()</div><div class=""><br class=""></div><div class="">…we would need `SomeClass` to inherit from `SomeBaseClass` and also *explicitly* re-declare its conformance to each of `ProtocolA`, `ProtocolB`, and `ProtocolC`, correct?</div><div class=""><br class=""></div><div class="">If so, IMHO, this would be a change for the worse in the context of UI-level code; it’s quite possible there'd be no specific class that could be used as `$SomeClass` (without a lot of busywork re-declarations of conformance); moreover, you’d have to do it again for each concrete type you expected to use with `Whatever`, which seems like a rather non-generic form of generics. But I might be misunderstanding.</div><div class=""><br class=""></div><div class="">Finally, as an observation, there are at least two different “flavors” of classes:</div><div class=""><br class=""></div><div class="">- value-ish classes, which mostly exist to represent some value (lots of Foundation, many “model classes”, etc.)</div><div class="">- identity-ish classes, which are specifically used as “entities” (UIView, CALayer, NSURLSession, NSOperation, etc.)</div><div class=""><br class=""></div><div class="">…and whereas the `value-ish` classes are often hitting awkward issues due to how protocol conformance and class inheritance interact, the identity-ish classes *largely* aren’t (with the main exception being due to inheriting `NSCoder` and perhaps `NSCopying`, which although awkward don’t seem awkward enough on their own to merit such a sweeping language-level change; a smaller change could address these).</div><div class=""><br class=""></div><div class="">In the identity-ish scenario I think subclasses inheriting their parent's protocol inheritance is *exactly* the usual — and useful — case, and thus I’d hope any solution to the issues encountered by value-ish classes would avoid making the creation-and-use of identity-ish classes too much more difficult / more tedious; please keep in mind that the “common” case in reports-of-problems may not be the “common” case in the field; when things work as-expected you may not hear as much about them, because there’s not much to say.</div><br class=""><div><blockquote type="cite" class=""><div class="">On Dec 10, 2015, at 8:04 PM, Joe Groff via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I've had a number of twitter conversations with users who have valiantly fought our type system and lost when trying to make their protocols interact well with non-final classes. A few from recent memory:<div class=""><br class=""></div><div class="">- Rob Napier struggling to implement a `copy` method that works well with subclasses: <a href="https://twitter.com/cocoaphony/status/660914612850843648" class="">https://twitter.com/cocoaphony/status/660914612850843648</a></div><div class="">and making the observation that the interaction of protocol conformance and subtyping is very difficult to teach.</div><div class=""><br class=""></div><div class="">- Matt Bischoff trying to make Cocoa class clusters retroactively conform to his factory protocol:</div><div class=""><a href="https://twitter.com/anandabits/status/664294382774849536" class="">https://twitter.com/anandabits/status/664294382774849536</a></div><div class=""><br class=""></div><div class="">and Karl Adam trying to do the same:</div><div class=""><a href="https://gist.github.com/thekarladam/c3094769cc8c87bf55e3" class="">https://gist.github.com/thekarladam/c3094769cc8c87bf55e3</a></div><div class=""><br class=""></div><div class="">These problems stem from the way protocol conformances currently interact with class inheritance—specifically, that if a class conforms to a protocol, then all of its possible derived classes also conform. This seems like the obvious way things should be, but in practice it ends up fighting how many classes are intended to be used. Often only a base class is intended to be the public interface, and derived classes are only implementation details—Cocoa class clusters are a great example of this. The inheritance of protocol conformances also imposes a bunch of knock-on complexity on conforming classes—initializer requirements must be satisfied by `required` initializers (which then must be overridden in all derived classes, to the pain of anyone touching an NSCoding-inherited class), and methods often must return dynamic `Self` when they'd really prefer to return the base class.</div><div class=""><br class=""></div><div class="">To mitigate these issues, I'd like to float the idea that protocol conformances *not be* inherited by default. If you declare a class as conforming to a protocol, only exactly that class can be bound to a type parameter constrained by that protocol:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">protocol Runcible {}</div></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">class A: Runcible { }</div><div class="">class B { }</div><div class=""><br class=""></div><div class="">func foo<T: Runcible>(x: T) {}</div><div class=""><br class=""></div><div class="">foo(B()) // calls foo with T == A</div><div class=""><br class=""></div></blockquote><div class="">Since subclasses are still subtypes of the base class, in many cases client code won't have to change at all, since derived instances can implicitly upconvert to their conforming base class when used in protocol types or generics that only the base class conforms to. (There are cases like if the type parameter appears in a NonCovariant<T> type where this isn't possible, though.) Protocol requirements for a non-inherited conformance don't need to be `required` initializers, or maintain covariant returns:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">protocol Fungible {</div><div class=""> init()</div><div class=""> static func funged() -> Self</div><div class="">}</div><div class=""><br class=""></div><div class="">class C: Fungible {</div><div class=""> init() {} // Non-required init is fine, since subclasses aren't directly Fungible</div><div class=""><br class=""></div><div class=""> // Non-Self return is fine too</div><div class=""> class func funged() -> C { return C() }</div><div class="">}</div><div class=""><br class=""></div></blockquote>An individual subclass that wanted to refine the conformance could do so by `override`-ing it, and providing any necessary covariant overrides of initializers and methods:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">class D: C, override Fungible {</div><div class=""> // D must provide its own init()</div><div class=""> init() { super.init() }</div><div class=""><br class=""></div><div class=""> // D must override funged() to return D instead of C</div><div class=""> override class func funged() -> D { return D() }</div><div class="">}</div><div class=""><br class=""></div></blockquote>And if a class hierarchy really wants to impose a conformance on all possible subclasses, as happens today, we could let you opt in to that:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class="">class E: required Fungible {</div><div class=""> // init() must be required of all subclasses</div><div class=""> required init() { }</div><div class=""><br class=""></div><div class=""> // funged() must return a covariant object</div><div class=""> class func funged() -> Self { return Self() }</div><div class="">}</div><div class=""><br class=""></div></blockquote>This is undoubtedly a complication of the language, but I think it might let us more accurately model a lot of things people seem to want to do in practice with class hierarchies and protocols, and it simplifies the behavior of the arguably common case where inheritance of the conformance isn't desired. What do you all think?<div class=""><br class=""></div><div class="">-Joe</div>
<img src="https://u2002410.ct.sendgrid.net/wf/open?upn=9EwXyNl81W9TT3yZ17PL28-2Be7Ks-2FXLjqa0dZcsddi5aWrQGhY6Rksd3tfGzCVyjaNTX-2FEkrpyJh4YxjhgXpMDTCLCWD3TBVuIS5x3M-2FHfOz2Y2vYfJJ4sdoW-2FwKoHrz0gpITSfmiiYbuxOcdJ95gfa-2FbtDs4BsnrpFCqlRwi8S3WvZeml4oqmqF8KjLl-2FhbLsTKAxyDxq22gE9C-2BnO78oqIswuHuuX4LnHXkZzILt1s-3D" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;" class="">
</div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>