<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="">Some very good points. But I’ll concentrate on what I disagree on to see where we can go from there.</div><br class=""><div><blockquote type="cite" class=""><div class="">On 23 Feb 2017, at 22:56, Nevin Brackett-Rozinsky 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=""><div dir="ltr" class=""><div class=""><b class="">Introduction</b></div><div class=""><br class=""></div>There has been a deluge of discussion about access levels lately, all attempting to simplify the situation. Shortly after Swift 3 was released, many people realized that the new access modifier story was far more complex than the old one, and expressed the belief that the changes may have been a mistake.<div class=""><br class=""></div><div class="">In the months that followed, more and more people came to share the same view, and stage 2 of Swift 4 has seen a profusion of proposals addressing the issue. These proposals are generally small and focus on changing just one aspect of access control. However, given the situation we are in now, it is important to look at the problem in its entirety and arrive at a cohesive solution.<div class=""><br class=""></div><div class=""><b class="">Background</b><br class=""><div class=""><br class=""></div><div class="">During the Swift 3 timeframe there were lengthy debates about access control. The end results were to adopt <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0025-scoped-access-level.md" class="">SE-0025</a>, which introduced the ‘fileprivate’ keyword and made ‘private’ scope-based, and <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0117-non-public-subclassable-by-default.md" class="">SE-0117</a>, which made ‘public’ classes closed by default and introduced the ‘open’ keyword. At the time, there was broad agreement (and some dissent) that these were the right changes to make.</div><div class=""><br class=""></div><div class="">That belief, as well as the numerous arguments which led to it, were well-informed and thoughtfully considered. However, due to the inevitable linear nature of time, they were not based on first-hand experience with the new changes. Upon the release of Swift 3, we all gained that first-hand experience, and it quickly became apparent to many people that the new access control system was needlessly complicated, and not at all the improvement it had been heralded as.</div><div class=""><br class=""></div><div class="">Rather than make it easy to encapsulate implementation details of related types across multiple files, we had instead doubled down on requiring that many things go in a single file or else reveal their secrets to the entire module. Even worse, the new scope-based ‘private’ discouraged the preferred style of using extensions to build up a type. To cap it off, we went from needing to know two access modifier keywords (‘public’ and ‘private’) to needing to know four of them (‘private’, ‘fileprivate’, ‘public’, and ‘open’) without even providing a way to share details across a small number of related files.</div></div><div class=""><br class=""></div><div class=""><b class="">Motivation – Overview</b></div><div class=""><br class=""></div><div class="">Many different ideas for access control have been expressed on the Swift Evolution mailing list. Some people want ‘protected’ or ‘friend’ or ‘extensible’ or various other combinations of type-based visibility. Other people (*cough* Slava) see no need for any levels finer than ‘internal’ at all. The common points of agreement, though, are that ‘fileprivate’ is an awkward spelling, and that the whole system is too complicated.<br class=""></div><div class=""><br class=""></div><div class="">It is important that we as the Swift community are able to recognize our mistakes, and even more important that we fix them. We originally thought that the Swift 3 access control changes would be beneficial. However, experience with them in practice has shown that not to be the case. Instead, the language became more complex, and that has real costs. It is time for a simplification.</div><div class=""><br class=""></div><div class="">The prevailing view from recent discussions is that there should be just one access level more fine-grained than ‘internal’, and it should be spelled ‘private’. Let us leave aside for the moment what its exact meaning should be, and consider the other end of the scale.</div><div class=""><br class=""></div><div class=""><b class="">Motivation – Rethinking ‘public’</b></div><div class=""><br class=""></div><div class="">Prior to Swift 3 we had just one access level more broad than ‘internal’, and for simplicity of the model it would be desirable to achieve that again. However, SE-0117 raised the point that certain library designs require a class to have subclasses within the defining module, but not outside. In other words, client code should not be able to create subclasses, even though they exist in the library. Let us be clear: this is a niche use-case, but it is important.</div><div class=""><br class=""></div><div class="">The most common situations are that a class either should not be subclassable at all—in which case it is ‘final’—or that it should be subclassable anywhere including client code. In order for a library to need a publicly closed class, it must first of all be using classes rather than a protocol with conforming structs, it must have a hierarchy with a parent class that is exposed outside the module, it must have subclasses of that parent class within the module, and it must also require that no external subclasses can exist. Putting all those criteria together, we see that closed classes are a rare thing to use. Nonetheless, they are valuable and can enable certain compiler optimizations, so we should support them.</div></div></div></div></blockquote><div><br class=""></div><div>The whole design and motivation behind SE-0117 is that it is not rare to want classes to be freely subclass-able internally while being closed to subclassing externally. Do you have arguments to contradict the lengthy points mentioned in the SE-0117 motivation section? The reasons for the rarity above do not make much sense to me:</div><div><br class=""></div><div><ul class="MailOutline"><li class=""><i class="">"it must first of all be using classes rather than a protocol with conforming structs”: </i>we are discussing the merits of the different forms of subclassing prevention (public vs final), so I don’t see the reason for mentioning structs.</li><li class="">"<i class="">it must have a hierarchy with a parent class that is exposed outside the module</i>”: I don’t see why it requires a hierarchy. You might want the soft-default of public to apply to non-subclassed classes which you might subclass in a later version of the library.</li></ul></div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class="">Currently, the spelling for a closed class is ‘public’. This makes it very easy for library authors to create them. However, since they are a niche feature and most of the time ‘final’ is a better choice, we do not need to dedicate the entire ‘public’ keyword to them.</div><div class=""><br class=""></div><div class="">Moreover, object-oriented programming is just as much a first-class citizen in Swift as protocol-oriented programming is, so we should treat it as such. Classes are inherently inheritable: when one writes “class Foo {}”, then Foo has a default visibility of ‘internal’, and by default it can have subclasses. That is a straightforward model, and it is easy to work with.</div><div class=""><br class=""></div><div class="">If subclasses are to be disallowed, then Foo should be marked ‘final’; if Foo is exported to clients then it should be marked ‘public’; and if both are true then Foo should be ‘public final’. This covers all the common cases, and leaves only the narrow corner of closed classes to consider. Per the motivation of SE-0117, that case is worth handling. Per our collective experience with Swift 3, however, it is not worth the added complexity of its own access modifier keyword. We need a better way to spell it.</div><div class=""><br class=""></div><div class="">One of the reasons ‘public’ was previously chosen for closed classes is to provide a “soft default” for library authors, so they can prevent subclassing until they decide later whether to allow it in a future release. This is a misguided decision, as it prioritizes the convenience of library authors over the productivity of application developers. Library authors have a responsibility to decide what interfaces they present, and we should not encourage them to release libraries without making those decisions.</div><div class=""><br class=""></div><div class="">Moreover, we need to trust client programmers to make reasonable choices. If a library mistakenly allows subclassing when it shouldn’t, all a client has to do to work with it correctly is *not make subclasses*. The library is still usable. Conversely, if a library mistakenly prohibits subclassing, then there are things a client *should* be able to do but cannot. The harm to the users of a library is greater in this last case, because the ability to use the library is compromised, and that diminishes their productivity.</div><div class=""><br class=""></div><div class="">We should not make “soft defaults” that tend to negatively impact the clients of a library for the dubious benefit of enabling its author to procrastinate on a basic design decision. If someone truly wants to publish a library with a closed class, then we should support that. But it should be an intentional decision, not a default.</div></div></div></div></blockquote><div><br class=""></div><div>That’s a bit harsh. For me, it has nothing to do with procrastination, but with being <b class="">safe-by-default</b>, which is an important Swift goal. You are arguing for <b class="">useful-by-default</b>, which is only a different priority.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class=""><b class="">Motivation – Rethinking ‘final’</b></div><div class=""><br class=""></div><div class="">The question then comes to spelling. It is evident that preventing subclasses is closely related to being ‘final’. One possibility, then, is to allow the ‘final’ keyword to take a parameter. The parameter would be an access level, to indicate that the type acts like it is final when accessed from at or above that level.</div><div class=""><br class=""></div><div class="">In particular, ‘final(public)’ would mean “this class cannot be subclassed from outside the module”, or in other words “this class appears final publicly, although it is nonfinal internally”. This approach is more powerful than a ‘closed’ keyword because it also allows ‘final(internal)’, meaning “this class appears final to the rest of the module, although it can be subclassed privately”.</div></div></div></div></blockquote><div><br class=""></div><div>As you can guess, I’m not a fan of this change :) I know that final has a weird place in the language now, but I’m relatively happy with the public/open design.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class=""><b class="">Motivation – Rethinking ‘private’</b></div><div class=""><br class=""></div><div class="">Now let us return to ‘private’, which as discussed earlier should be the only modifier that is tighter than ‘internal’. The purpose of ‘private’ is to enable encapsulation of related code, without revealing implementation details to the rest of the module. It should be compatible with using extensions to build up types, and it should not encourage overly-long files.</div><div class=""><br class=""></div><div class="">The natural definition, therefore, is that ‘private’ should mean “visible in a small group of files which belong together as a unit”. Of course Swift does not yet have submodules, and is not likely to gain them this year. However, if we say that each file is implicitly its own submodule unless otherwise specified, then the model works. In that view, ‘private’ will mean “visible in this submodule”, and for the time being that is synonymous with “visible in this file”.</div></div></div></div></blockquote><div><br class=""></div><div>This is very counterintuitive for me. I’m a proponent for going back to a file-based private but your solution has two big disadvantages for me:</div><div><br class=""></div><div><ul class="MailOutline"><li class="">it makes impossible to have a file-based scope in submodules</li><li class=""><b class="">private</b> would have different semantics depending if its in a submodule or not. As a consequence:</li><ul class=""><li class="">it would be confusing to learn and use</li><li class="">it would make copy-pasting/moving files from a submodule to a top-module potentially breaking:</li></ul></ul><div class=""><br class=""></div><div class=""><b class="">x.swift in submodule A</b></div><div class="">class X {</div><div class=""> private init() {}</div><div class="">}</div><div class=""><br class=""></div><div class=""><b class="">y.swift in submodule A</b></div><div class="">class Y {</div><div class=""> let x = X()</div><div class="">}</div><div class=""><br class=""></div><div class="">This above code would stop working when moved to a top-module scope because X’s initializer would become un-accessible to Y. That’s just one example, but I’d really like to stress that changing the scope of an access-level depending on the submodule context seems like a really bad idea to me.</div></div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><div class="">Although this does not immediately enable lengthy files to be separated along natural divisions, it does lay the groundwork to allow doing so in the future when submodules arrive.</div><div class=""><br class=""></div><div class=""><b class="">Motivation – Summary</b></div><div class=""><br class=""></div><div class="">By looking at access control in its entirety, we can adopt a system that empowers both library authors and client programmers to organize their code in a principled way, and to expose the interfaces they want in the places they need. The complexity of the Swift 3 visibility story, which many people now regret creating, will be replaced by a far simpler model which in several respects is even more powerful.</div><div class=""><br class=""></div><div class="">Notably, being able to parameterize ‘final’ lets classes be closed not just externally, but also in the rest of the module outside the ‘private’ scope if desired. Furthermore, defining ‘private’ as being scoped to a group of related files means that, as soon as we get the ability to create such groups, it will no longer be necessary to write large files just to keep implementation details hidden.</div><div class=""><br class=""></div><div class=""><b class="">Recommendations</b></div><div class=""><br class=""></div><div class="">To recap, the ideas presented here focus on simplifying access control while still supporting important use cases such as closed class hierarchies. The indicated design uses just three familiar access keywords:</div><div class=""><br class=""></div><div class="">‘private’, to restrict visibility to a group of files, or just one file until we get that capability.</div><div class=""><br class=""></div><div class="">‘internal’, which is the default and does not have to be written, for module-wide visibility.</div><div class=""><br class=""></div><div class="">‘public’, to make visible outside the module, including the ability to subclass.</div><div class=""><br class=""></div><div class="">Additionally, the design allows ‘final’ to take any one of those visibility levels as a parameter, to indicate that the type should be treated as ‘final’ at and above the specified scope. Thus ‘final(public)’ prevents subclassing outside the module, while ‘final(internal)’ prevents it outside the ‘private’ scope. For consistency, ‘final(private)’ is also permitted, although it means the same thing as ‘final’ by itself.</div><div class=""><br class=""></div><div class=""><b class="">Conclusion</b></div><div class=""><br class=""></div><div class="">The Swift 3 access situation is harmful—as evidenced by the myriad calls to fix it—not just because of its excessive complexity, but also because it prioritizes convenience for library authors over utility for their clients, and because it has no natural way to accommodate splitting large files into smaller ones while preserving encapsulation.</div><div class=""><br class=""></div><div class="">We have an opportunity now to correct a mistake we have made, and to set a precedent that we *will* correct our mistakes, rather than continue down an undesirable path simply because it seemed like a good idea at the time. When real-world experience demonstrates that a change has taken us in the wrong direction, we can and should update our decisions based on that new experience.</div><div class=""><br class=""></div><div class="">Therefore, in the situation at hand, we should reconsider our access modifier story and choose a model which is both simple and powerful. I have presented here my best efforts at describing such a system, and I offer it as one possible way to move forward.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""> – Nevin<br class=""></div></div></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>