<div dir="ltr">On Tue, Sep 6, 2016 at 8:06 PM, Jonathan Hull <span dir="ltr">&lt;<a href="mailto:jhull@gbis.com" target="_blank">jhull@gbis.com</a>&gt;</span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><span class="gmail-"><blockquote type="cite"><div><div style="white-space:pre-wrap">I argue the same thing applies here. Currently, protocols constrain the API of conforming types. If a designer has reasoned through their design correctly, then all is well. If a designer has made bad design choices, then conforming types are constrained to have bad API design. Is that a bug or a feature? It is of course a bug on the part of the designer for having designed a bad protocol. But I think it is arguably a feature on the part of Swift for refusing to allow code to circumvent the protocol&#39;s design, because a protocol that is out of the end user&#39;s control is also likely a protocol for which the default implementations are opaque to that user and can&#39;t be reasoned through by that user.</div></div></blockquote><div><br></div></span><div>There seem to be two schools of thought in this community:  Those that want to empower good design, and those that want to prevent/punish bad design.</div><div><br></div><div>They often look similar, but they are subtly different.  One treats the programmer as an intelligent adult, who may make mistakes, and would appreciate reminders about the possibility of problems.  The other treats the programmer as a child who must be protected from themselves.  I am strongly in the first camp.</div></div></div></blockquote><div><br></div><div>I think this is quite an unfair characterization. When a programming language can guarantee that certain things won&#39;t occur, it isn&#39;t about punishing anyone; rather, those who have to read the code and reason about its behavior gain tangible benefits from such constraints.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div>We should provide warnings and force conflicts to be resolved in some way, but ultimately we need to trust the programmer to do the right thing. Also, as people kept saying during the &#39;closed by default&#39; discussion, if they screw up and rename something in a confusing way, then other people will complain to them and they will change it.</div><span class="gmail-"><div><br></div><br><blockquote type="cite"><div><div style="white-space:pre-wrap">I&#39;m not entirely sure on my position, though. To be convinced otherwise, I&#39;d need to see a compelling real-world use case that demonstrates all of the following:<br><br>- Safety: perhaps, with protocols, unlike class inheritance, it is almost always safe to make these kinds of changes unanticipated by the original author--it would be good to see evidence either way<br></div></div></blockquote><div><br></div></span><div>You may find this paper interesting:</div><div><a href="http://scg.unibe.ch/archive/papers/Scha02bTraits.pdf" target="_blank">http://scg.unibe.ch/archive/<wbr>papers/Scha02bTraits.pdf</a></div><div><br></div><div>It discusses traits, which are basically Swift’s protocols, plus the ability to handle conflicts.  It discusses at length the issues that arise with other systems.</div></div></div></blockquote><div><br></div><div>Thanks for the link. I&#39;ll need to study this further. For the moment, one initial thought: one key point in this paper is that conflict resolution for traits hinges on the fact that they do not access state, and so the diamond problem does not occur. </div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><br></div><div>This is a very common problem that only a few programming languages have found successful solutions to.  I would also point you to Eiffel’s model for multiple inheritance (select, rename, undefine, export).  I think if we keep working on it, we can find a uniquely swift-feeling solution which solves the issue :-)</div><span class="gmail-"><div><br></div><br><blockquote type="cite"><div><div style="white-space:pre-wrap">- Necessity: earlier, examples were cited where at least one of two conflicting protocols were under the user&#39;s control; in that case, this feature isn&#39;t needed, because that protocol&#39;s requirements can be trivially renamed<br></div></div></blockquote><div><br></div></span><div>I ran into a case of the diamond problem with protocols again today in a real world project.  Both classes were under my control, but I wasn’t able to avoid the name conflict because they had a common ancestor.</div></div></div></blockquote><div><br></div><div>Can you explain this in more detail? Were you working with protocols or classes?</div><div><br></div><div>If a protocol inherits from two protocols with a common ancestor, the protocol requirements from that ancestor cannot possibly conflict with themselves. Are you talking about default implementations? If so, there&#39;s another thread (recent, but dormant now) suggesting new syntax to call a named default implementation from an implementation in a conforming type (and by extension (har har), from a default implementation in an inheriting protocol). I believe that the suggestion had a pretty positive reception, and unless I&#39;m mistaken, it would address this particular issue (which I agree is currently problematic).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div>I was able to solve it by copy/pasting the winning method definition, but this is definitely not ideal, since I must now maintain code in two places which makes upkeep/changes more difficult.</div><div><br></div><div>What I want here is the ability to notate the winner in my inheriting protocol.</div><span class="gmail-"><div><br></div><br><blockquote type="cite"><div><div style="white-space:pre-wrap">- Good design: show me that the design patterns enabled by the feature are better than what is possible with workarounds; if a renaming or hiding feature is expedient, but an alternative approach would promote less fragile or more Swifty design, then we should be exploring ways to simplify or promote an alternative approach; one consideration, for example, is that naming of anything is hard, and the current fact that protocols *need* to have well-chosen member names encourages designers to think hard--but if renaming becomes possible, could we simply be enabling less thoughtful protocol design with little benefit (to evaluate this, we would need some sense of the answer to the necessity question above)?</div></div></blockquote><div><br></div></span><div>Your suggestion that the framework designers should have to go back and refactor their code because of another framework is an indication of coupled design and bad code smell.  Suddenly my protocols have to know about all of the other protocols being used (or which could theoretically be used) and I have to design around them.  This is problematic.  Ideally, I should be able to design each protocol in a way which makes the most sense semantically for the use of that protocol.  Any contortions I have to make to the design away from the ideal are something to be avoided where possible.  I know we are used to making these contortions, but is it really the best design (or is it a form of Stockholm syndrome)?</div></div></div></blockquote><div><br></div><div>I see this differently. Are you arguing that protocols should be designed *without* consideration of how they compose with other protocols? By the same token, should types be designed *without* consideration of how they are used? If users of my library (which might be just myself, dogfooding) find that the API is cumbersome, are you arguing that I *shouldn&#39;t* go back and redesign the API for the next major version, because I&#39;d be coupling the design of my API to the needs of my end users?</div><div><br></div><div>To use your analogy, what you characterize as grotesque contortions are what I&#39;m arguing is a graceful dance.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><br></div><div>Here is an article on Eiffel’s multiple inheritance system which makes the point well:</div><div><a href="https://archive.eiffel.com/doc/manuals/technology/bmarticles/joop/multiple.html" target="_blank">https://archive.eiffel.com/<wbr>doc/manuals/technology/<wbr>bmarticles/joop/multiple.html</a></div><div><br></div><div>Some selected passages:</div><div><blockquote type="cite"><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">One of the differences is particularly important if you take a software engineering viewpoint and consider these solutions in relation with the software development lifecycle. If the developers first implement simple menus only, and only then realize the need for walking menus, using tree solution means that they must go back to the initial definition of class <font color="navy"><i>MENU</i></font> and rework it extensively. This brings disorder into the development process.</p><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">With the multiple inheritance solution, things are quite different. When the developers realize that a new kind of menu is needed, they simply define it by inheritance from <font color="navy"><i>MENU</i></font> and <font color="navy"><i>ENTRY</i></font>. The changes to existing software are minimal. This is an example of one of the key benefits of the object-oriented method, which would have to be renounced in the absence of multiple inheritance.</p></blockquote></div><div>(emphasis mine)</div><div><blockquote type="cite"><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">Assume you are in the inheritance-based reusability business and combine classes from two software suppliers, say one in New York and one in London. Sure enough, one day you are going to run into the problem of combining two classes that both have a feature called <font color="navy"><i>foo</i></font>, to use as example one of these nice evocative names that programmers are fond of.</p><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">But this is not a conceptual problem! It merely results from an unfortunate choice of names. If the parent programmers had chosen other names, differing by just one letter -- influenced perhaps by local conditions, they might have used <font color="navy"><i>zoo</i></font> in New York and <font color="navy"><i>fog</i></font> in London --, the problem would never have arisen.</p><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">This suggests the two key observations on this problem:</p><ul style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal"><li style="font-size:12.288px;margin-bottom:0px">First, it is purely a <i>syntactical</i> problem, due to conflicting name choices. It has nothing to do with the fundamental properties of the classes involved.</li><li style="font-size:12.288px;margin-bottom:0px">Second, <b>nothing is wrong with the parents</b>; each is perfectly consistent as it stands. The &quot;culprit&quot; is the common heir, which tries to combine two classes that are incompatible as they stand. So <b>the heir should also be responsible for the solution</b>.</li></ul></blockquote></div></div></div></blockquote><div><br></div><div>Unless I&#39;m mistaken, the generally accepted wisdom that&#39;s emerged since the publication of that article in 1988 is that multiple inheritance is perhaps not the wisest choice. Today, Swift supports OOP but eschews multiple inheritance. To the extent that we extend Swift&#39;s support of POP, it should not be to restore the same pitfalls of multiple inheritance under the guise of a different paradigm.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div></div><div><br></div><div>It also includes an example of how renaming can make a design more consistent.  You can use a protocol for it’s functionality (Tree-based calculation) while providing an end-user interface that makes sense to your domain. </div></div></div></div></blockquote><div><br></div><div>Again, I understand the motivation. My point is, currently, conformance to a protocol guarantees a certain interface for the conforming type. Adding the ability to use a protocol for its functionality *without* being constrained to provide an interface *necessarily entails the loss* of a certain feature of protocol conformance today: namely, the guarantee of that interface on the concrete type.<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div><div><blockquote type="cite"><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">Although purely syntactical, renaming is not limited in its applications to the resolution of name clashes. Among its other uses, one deserves a particular mention because it sheds more light into the meaning and power of inheritance. This is renaming used to define consistent class interfaces.</p><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">The following example is typical. Again, it will illustrate techniques used in the Eiffel Graphical Library. Assume we want to describe rectangular windows that can be arbitrarily nested. The corresponding class, say <font color="navy"><i>WINDOW</i></font>, will have a large number of features, which can be grouped into two categories:</p><ul style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal"><li style="font-size:12.288px;margin-bottom:0px">Graphical features: <font color="navy"><i>height, width, position, move, scale</i></font> and the like.</li><li style="font-size:12.288px;margin-bottom:0px">Hierarchical features, having to do with the tree structure of nested windows: <font color="navy"><i>add_subwindow, remove_subwindow, superwindow</i></font> and the like.</li></ul></blockquote>[…]<br><div><blockquote type="cite"><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal"> Features inherited from <font color="navy"><i>TREE</i></font>, in particular, will bear their tree names: <font color="navy"><i>insert_node, remove_node, parent_node</i></font> and so on. This is not acceptable to a client programmer like Ed who wants good, concrete window terminology.</p><p style="font-family:verdana,arial,geneva,helvetica,geneva,sans-serif;font-size:12.288px;line-height:normal">With renaming, the solution is straightforward. Wendy ought to spend ten more minutes polishing the interface, so that clients of class <i>WINDOW</i> won&#39;t need to be aware of its tree ancestry:</p><pre style="margin-bottom:0px;font-size:11.9808px;line-height:normal"><font color="navy">
        <b>class</b><i> WINDOW</i></font> <b>inherit</b>

                <i>RECT_SHAPE</i>
                        <b>rename</b> ...

                <i>TREE</i>
                        <b>rename</b><i>
                                insert_node <b>as</b><i> add_subwindow</i>,
                                remove_node <b>as</b><i> delete_subwindow</i>,
                                parent_node <b>as</b><i> superwindow</i>,
                                ...
        </i><b>feature</b>

                ...

        <b>end</b> -- class <i>WINDOW</i></pre></blockquote><div><br></div></div></div><div>I think we have an opportunity here to actually solve this problem by looking at the solutions which came before and then creating something uniquely swift.</div></div></div></div></blockquote><div><br></div><div>Doesn&#39;t the same quotation argue for multiple inheritance of classes and renaming of class members in subclasses, neither of which are supported in Swift? Are you proposing to have those features too?</div><div><br></div><div>In this example, Wendy renames members on WINDOW for the express purpose of obscuring its ancestry. This makes Ed happy because he likes particular names for methods on WINDOW. However, it certainly makes it more difficult to reason about what&#39;s actually going on when you call these methods. Where is add_subwindow implemented? You won&#39;t find it anywhere in the code. This is not an unmitigated win. It&#39;s a mixed bag at best; IMO, we&#39;re better off without such a feature.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word"><div><div>Thanks,</div><div>Jon</div><br></div><br></div></blockquote></div><br></div></div>