<div dir="ltr">Hi Brent,<div><br></div><div>I read through your proposal (skimmed it when it was posted, and then read through it closely when you brought it up again). It's a really interesting proposal and would be a strong addition to Swift, and I really think it deserves to be proposed as a formal alternative to abstract classes. I imagine it would garner considerably more support than has been shown in this thread (which has been more about the philosophy of POP than concrete solutions), and probably stands a decent chance of being accepted over this proposal (if either is accepted at all).</div><div><br></div><div>That being said, here are my personal thoughts and/or objections. (I don't hold any of these dearly, and would love to be convinced otherwise. I am all too aware that today's "this is too strange" language concepts may become tomorrow's indispensable tools :-)</div><div><br></div><div>First of all: I'm wary about making protocols even more conceptually complicated than they already are. The term 'protocol' already refers to two distinct concepts - protocols containing dynamically dispatched method requirements, like the Objective-C equivalent, and protocols with associated types that can only be used as constraints on generics. This is a perennial source of confusion (c.f. multiple blog posts on trying and failing to compare two 'Equatable' objects). Now we have default implementations for protocol methods, but those are statically dispatched and so the rules of whether the default implementation or a type's specific implementation are called have repeatedly surprised people (the IBM 'Swift pitfalls' article is a good example, as are the occasional proposals to change this behavior).</div><div><br></div><div>I see the orthogonality of abstract classes to protocols as a desirable thing: abstract classes/methods are a simple concept intended for a specific use case and (in my opinion) well-suited towards fulfilling that use case.</div><div><br></div><div>My second concern involves the ramifications of adding dynamic dispatch to something like these augmented protocols (or multiple inheritance of implementation in general, e.g. with mixins). How are calls to 'super', or usage of an implementation defined in a supertype resolved? In the simple case:</div><div><br></div><div>protocol P : C {</div><div> override func foo() { super.foo() }</div><div>}</div><div><br></div><div>class C {</div><div> func foo() { ... }<br>}</div><div><br></div><div>class C1 : C, P {</div><div> override func foo() { super.foo() }</div><div>}</div><div><br></div><div>If C1().foo() is called, what is the call chain? C1's foo(), then P's, then C's? What if you have multiple protocols that sit on the same level?</div><div><br></div><div>protocol Pa : C1 {</div><div> override func foo() { super.foo() }</div><div>}</div><div>protocol Pb : C1 {</div><div> override func foo() { super.foo() }</div><div>}</div><div><br></div><div>class C2 : C1, Pa, Pb {</div><div> override func foo() { super.foo() }</div><div>}</div><div><br></div><div>At some point we'd have to deal with method resolution order a la multiple inheritance, and I think whether having multiple inheritance and requiring developers to learn how Swift linearizes method calls is a good thing is worth a major discussion itself. Another option would be to restrict a class to conform to at most one protocol with an inheritance requirement, but that would remove one of the most compelling advantages of the protocol-based solution - the ability for a class to fulfill multiple abstract type requirements.</div><div><br></div><div>(Default protocol implementations aren't subject to this problem; if you try to create a situation in which there would be an ambiguity as to which impl was called the compiler will complain, and there is no concept of 'super' there.)<br></div><div><br></div><div>Again, I see the limitation to single inheritance of abstract classes in this regard as a virtue. Most of the things I want to model using abstract classes are most clearly expressed in terms of a single inheritance of implementation class hierarchy, in which responsibility for implementation is neatly split into two categories: requirements implemented at the abstract class (or above), and requirements implemented below the abstract class. (This is recursive; responsibility for requirements in the second category can again be split in two by further abstract classes, although the usual deep class hierarchy caveats apply.)</div><div><br></div><div>Anyways, I hope this helps explain my reasoning wrt abstract classes vs protocols. At the very least, I hope it allows you to sharpen your proposal and make it even better.</div><div><br></div><div>Best,</div><div>Austin</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Mar 3, 2016 at 2:16 PM, Brent Royal-Gordon via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">> Yes, a protocol will force you to provide an implementation. However, a protocol *by itself* cannot force the implementor of a class to provide an implementation unless that class declares itself in conformance with that protocol.<br>
><br>
> ONLY an abstract class, ON ITS OWN, can force concrete subclassers to provide an implementation for a given thing.<br>
><br>
> Protocols can't do this, because if you forget to declare conformance with the protocol, the compiler can't enforce anything. This pushes what could be caught at compile-time to a runtime (potentially crashing) problem.<br>
<br>
</span>If you forget to declare inheritance from an abstract class, it won't force you to implement those methods, either.<br>
<br>
I do completely understand your concerns about the class + protocol workaround which is currently necessary here. That's why I'm proposing we allow protocols to require inheritance, override members, create stored properties, etc. That would allow the protocol to *wholly* replace the base class, taking on the job of providing both the concrete portions of the implementation and the requirements subtypes need to satisfy.<br>
<span class=""><br>
> Abstract classes enforce a requirement that a given portion of the class hierarchy provide an implementation of X.<br>
<br>
<br>
</span>Protocols don't operate entirely within the class hierarchy, but Swift's type system is broader than just a class hierarchy. In the context of the entire type (directed acyclic) graph, they play the same role of enforcing requirements on subtypes. And with the proper features in place, that's all we really need.<br>
<span class=""><br>
> Swift isn't ONLY about protocol oriented programming. Classes are here for a reason, and they should not be relegated to second-class status where people refuse to consider class-only functionality just because the concept can't be shoehorned into something protocol-related.<br>
<br>
</span>For what it's worth, even though I prefer the protocol solution in this case, I share your frustration with the reviews that do little more than recite "Protocol-Oriented Programming" as a slogan. I believe there are strong, convincing reasons to prefer the protocol approach here, but none of them are captured by that phrase.<br>
<br>
But I think the pro-abstract class side is suffering from the same problem. Why is it so important that this feature be expressed strictly within the class hierarchy? Both extending classes to support abstract class-style functionality *and* extending protocols to support abstract class-style functionality bring abstract class-style functionality into the language. I'm not saying they're completely interchangeable and equivalent—there are differences, there are reasons to prefer one over the other—but they ultimately provide most of the same functionality.<br>
<br>
If we went the abstract class route instead of the protocol route, I would be disappointed—I would think that we took the well-trodden path rather than explore something that was better and more consistent with the language. But I wouldn't feel like there was a gaping void in the language, because abstract classes *would* fulfill most of the use cases here.<br>
<br>
I don't understand why you and the other abstract class supporters don't feel the same way—why you seem to treat this like a fight for your lives, not like a fight for the modestly superior design. The only thing that makes sense to me is dogmatism, but I want to give you more credit than that.<br>
<br>
So explain to me: if this code worked:<br>
<br>
protocol ActivityViewControlling: UIViewController {<br>
func retrieveText() -> String<br>
}<br>
extension ActivityViewControlling {<br>
@IBOutlet var messageLabel: UILabel!<br>
<span class=""><br>
override func viewWillAppear(animated: Bool) {<br>
super.viewWillAppear(animated)<br>
</span> messageLabel.text = retrieveText()<br>
}<br>
}<br>
<br>
What would you feel was missing compared to this?<br>
<br>
abstract class ActivityViewController: UIViewController {<br>
abstract func retrieveText() -> String<br>
<br>
@IBOutlet var messageLabel: UILabel!<br>
<span class=""><br>
override func viewWillAppear(animated: Bool) {<br>
super.viewWillAppear(animated)<br>
</span> messageLabel.text = retrieveText()<br>
<div class="HOEnZb"><div class="h5"> }<br>
}<br>
<br>
--<br>
Brent Royal-Gordon<br>
Architechies<br>
<br>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</div></div></blockquote></div><br></div>