Very well written.

Personally I am in favor of very simple systems and to put the responsibility 
for an application by the application developer (not a library developer). 
Though I understand that in some cases (Apple!) the developer is the 
end-customer, and this creates special circumstances warranting concessions on 
the achievable simplicity.

While it is possible to create an even simpler access level system, I think the 
system you proposed is probably the best compromise between “simple enough” and 
“complex enough”, and I could certainly live with it.

The final word (imo) should come after the submodule’s discussion reaches its 
inevitable peak.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl





> On 23 Feb 2017, at 22:56, Nevin Brackett-Rozinsky via swift-evolution 
> <[email protected]> wrote:
> 
> Introduction
> 
> 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.
> 
> 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.
> 
> Background
> 
> During the Swift 3 timeframe there were lengthy debates about access control. 
> The end results were to adopt SE-0025, which introduced the ‘fileprivate’ 
> keyword and made ‘private’ scope-based, and SE-0117, 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.
> 
> 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.
> 
> 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.
> 
> Motivation – Overview
> 
> 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.
> 
> 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.
> 
> 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.
> 
> Motivation – Rethinking ‘public’
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 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.
> 
> Motivation – Rethinking ‘final’
> 
> 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.
> 
> 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”.
> 
> Motivation – Rethinking ‘private’
> 
> 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.
> 
> 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”.
> 
> 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.
> 
> Motivation – Summary
> 
> 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.
> 
> 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.
> 
> Recommendations
> 
> 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:
> 
> ‘private’, to restrict visibility to a group of files, or just one file until 
> we get that capability.
> 
> ‘internal’, which is the default and does not have to be written, for 
> module-wide visibility.
> 
> ‘public’, to make visible outside the module, including the ability to 
> subclass.
> 
> 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.
> 
> Conclusion
> 
> 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.
> 
> 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.
> 
> 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.
> 
> 
>  – Nevin
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to