[swift-evolution] [Review] SE-0025 Scoped Access Level
brent at architechies.com
Mon Feb 29 04:45:39 CST 2016
> • What is your evaluation of the proposal?
I don't think it's a good idea.
I think the source of our disagreement is expressed in this paragraph from the proposal:
> It forces a one class per file structure, which is very limiting. Putting related APIs and/or related implementations in the same file helps ensure consistency and reduces the time to find a particular API or implementation. This does not mean that the classes in the same file need to share otherwise hidden APIs, but there is no way to express it with the current access levels.
This reflects a view that grouping APIs into files is a purely stylistic choice. You can move APIs around freely and organize them however you like. If Swift gets in the way of your preferred arrangement, Swift should be changed to allow it. The most important thing is that you be free to express yourself artistically through the medium of the file system.
I believe that view is mistaken. Swift has a very opinionated view of how code should be organized into files; they are as semantically meaningful as declaration blocks.
The purpose of a file is to implement one concern—essentially, one logical piece of the module's functionality with its own self-contained implementation details. A concern is not necessarily fully represented by a single type; a type may include several concerns, and several types may implement one concern. (It's not a coincidence that a file can contain several types and a type can be split across several files.) The precise boundaries of a concern are a little nebulous, especially when you build convenience APIs which don't depend on anything private, but it's usually roughly clear what they are.
When you want to use `local`, that usually means you're not organizing your code the way Swift thinks you should. It's no wonder Swift seems to not be expressive enough: You're fighting the language.
Now, there are two exceptions to this general rule, but I think they're both best handled in other ways.
The first: Sometimes Swift will not allow you to move certain things to separate files; for instance, only one file can declare stored properties on a type, and so stored properties must either be made more visible than they should be, or several concerns must be mixed into a single file. I think this is best handled by allowing extensions to declare stored properties and other such one-file-only constructs, rather than by complicating access control.
The second: Sometimes a particular concern has an especially complicated, self-contained "sub-concern" which has implementation details of its own. You would like to keep the sub-concerns implementation details private from the containing concern, but the sub-concern is *itself* an implementation detail of the containing concern, so you *also* want to keep the sub-concern private from other, unrelated concerns. In these cases, some sort of more nuanced access control would be better—but even then, I don't think `local` is actually a very good way to do it.
There's nothing about a declaration block that makes it a natural choice for scoping declarations. `local` hides the declaration from containing and sibling declaration blocks and exposes it to nested declaration blocks. But if concerns often transcend type boundaries, surely sub-concerns do as well, so `local` will often be either too limiting or not limiting enough.
To properly handle this problem, we would be better off coming up with some way to limit the scope of `internal` to only particular files which need to interface with that file's concern. `internal` would expose the API to your file's "clients"—by default all files in the module, but potentially narrowed down to a particular subset—while `private` would remain as something truly limited to a single file.
However, that approach is rather complicated, bordering on the horror of C++ `friend` declarations. Ultimately, I just don't think it's too large of a burden to say, "You have a three-level namespace, and anything that crosses files goes into `internal`; if something in `internal` is only meant to be used in a particular file, show some discipline."
> • Is the problem being addressed significant enough to warrant a change to Swift?
As I often say, the problem is arguably significant enough, but I don't think the solution is the right one.
> • Does this proposal fit well with the feel and direction of Swift?
I don't think so. Swift has strong, opinionated ideas about how code should be organized; this proposal doesn't follow that pattern.
> • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
I've used languages with file-based scoping, and languages with arbitrary lexical scoping, but not languages with both.
> • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
In addition to reading the present proposal, I also participated fairly extensively in discussions about it and read previously-posted reviews.
More information about the swift-evolution