[swift-evolution] final + lazy + fileprivate modifiers

Joanna Carter joanna at carterconsulting.org.uk
Fri Feb 17 16:15:12 CST 2017


> No, sorry, I don't agree with you.
> Current situation forces us to generate huge source files or write each 
> type in its own submodule/file. IMO it is very naturally to have a need to 
> protect access to some details *even* in the same file(please find David 
> Sweeris's answer in previous email in this thread. with current 'private' 
> he can *guarantee* that no code touches internal props even in the same 
> file), also many of us need a way to share some details only for 
> extensions/subtypes in other files in the same module/submodule even just 
> to organize code as *one* need/want and to express intention about who 
> should "touch" this code and get help from compiler when accidentally try 
> to use protected method/prop.
> 
> Someone, who want just internal/public can use only them in own code, no 
> problems. But many feels that they need a more detailed access levels to 
> structure their code, they find current situation not comfortable.

But I agree with you that there really is a need for better, what I would call, entity-based visibility.

In several other OO languages, this is not considered a problem and I fail to see why Swift should be any different, apart from the desire on some sides to make it different just for the sake of difference.

In a class hierarchy, I can declare stuff as private, knowing full well that nobody, but nobody, can "see" or touch that stuff outside of the declaring class. That is of paramount importance and central to the concept of information hiding.

In a class hierarchy, there is stuff that I want to hide from everybody and everything, except those classes that belong to the same hierarchy, which are allowed to have privileged access.

There are also times when I want to declare a base class that I am expecting users to derive from, especially an abstract class. I want a means of declaring that certain stuff can only be overridden by subclasses but, most definitely, not callable from any code outside of that hierarchy. For that, I express that intent by declaring such stuff as protected, thus preventing "unauthorised" use from outside the intended hierarchy.

And, it is in this respect, with the predominance of protocols and structs in Swift, that I want to express the same limitations of access to within any type (class, struct, enum) that implements a given protocol, or to within any extension to any type.

We are allowed to declare base functionality in an extension to a protocol, in much the same way as we would in an abstract class.

The difference between Swift protocols and abstract classes is that, with Swift protocols, I cannot, presently, limit access to "base" behaviour in the same way that I can in an abstract class. On the other hand, I cannot allow implementing types or extensions to access those base behaviours unless I put them in the same code file as the declaring protocol.

I am perfectly comfortable with the idea of module scope but fail to see the point of fileprivate scope, part from as a workaround to the lack of protected scope.

As you say, the purpose of scope/visibility is to inform and direct others of how your code is intended to be used. There are situations when internal scope is simply too great and private scope is too restricted ; as is fileprivate even (unless you don't mind bloated, monolithic files).

I believe that what you and I are not arguing for, is the introduction of what used to be called protected scope, as found only in classes ; in these days of protocols and structs, that would be too limiting and, by implication, more relevant to classes only.

Instead, for the wider gamut of types and increased focus on protocols instead of abstract classes, what is needed is the same kind of scope that will work with both class hierarchies and protocols and their extensions or implementations.

Thus, I would confirm that I would prefer to see a simple range of scopes :

1. private - only visible within the declaring type, including nested types.

2. extensible (formerly protected for classes) - visible within the declaring type plus : 
  a. for classes, visible in subclasses or extensions
  b. for protocols, structs and enums, visible in extensions to the declaring type

3. internal - visible within the declaring module

4. public - does what it says on the tin ; visible anywhere, both inside and outside the declaring module

5. removal of fileprivate as an excuse for not having extensible

I would also remind readers of the current peculiar situation with classes in Swift, where we have both final classes that totally prohibit derivation, but also a mishmash of scope and inheritance restriction with open and public.

Why is it that classes have both entity-based and module-based restriction on their derivation? What is wrong with just using final for those classes that we do not want to be derived from? I would rather see the extensible scope for "derivation" both inside and outside of the module, with the ability to use that same scope identifier in the case of class inheritance

And, for inheritance control in classes, retain the use of final and remove the conflation of visibility and inheritance control that "open vs public" gives us.

My lords, ladies and gentlemen, I commend to the house the KISS scope manifesto :-)

Unless there is a really good reason for making things more complicated, that is…

--
Joanna Carter
Carter Consulting



More information about the swift-evolution mailing list