Access control below public [not `protected`]

If OOP access control schemes are a non-goal, then what are the classical POP (protocol-oriented programming) and (enum and struct)-over-class based programming schemes?

Are there any languages that use a similar main programming paradigm as Swift? If so, we could try to see what their access control story is, and maybe derive something from it.

I think that what we're lacking here is a whole story, a model of how all the access control pieces fit together. That's why I think checking the solutions of others might benefit us a great deal.

2 Likes

Imho OOP is more than inheritance and "protected", and Swift is neither protocol- nor enum/struct oriented — those concepts just have a loud fanbase, but are no replacement for OOP.
Nonetheless, I generally agree that we should learn more from other languages... but I think access control is scorched earth, and at least everyone seems to be equally unhappy with it, so it might be a good compromise ;-)

I think this is a big threat for Swift in general:
Small additive changes which are favored by evolution have merit, but some improvements just need a big plan that doesn't fit in a single proposal.

7 Likes

This was already discussed and informally rejected last year because it complicated access control. To keep access control relatively sane, you need to have each access control represent a strict superset of the visibility of less visible access controls. Like that, modifying a declaration from a less visible to a more visible access will never break your code. That’s why the current private was redesigned as it is: if it allowed access across files, fileprivate would not have been a superset of its visibility.

1 Like

Not really.

If you check C#'s access modifiers you'll see that protected and internal don't contain one another. In fact, they can be composed, which puts the modifiers in a logical AND (protected internal means that you can only access the member from the same assembly, and only from a derived class).

This is a good counterexample for your point, I think. But it doesn't help us find a better access control story for Swift, because C#'s main paradigm is OOP and therefore it lends itself well to things like protected.

1 Like

Why do we need a better one? Just leave it. The last changes already did much damage from the clear model at the start.

I don't get why there is so much resistance to file based access control. It's a time proved way to do it, many languages supporting modules (ie: Pascal, Modula-2, Oberon-2, CmponentPascal) use it. Is the reason that many developers only know include-files and headers instead of full modules?

1 Like

Would you say the current one is ideal? I think what the OP's point is, in part at least, is that this is a conversation worth having. And I agree with him. I think we're not done yet with access control, and we should see something change about it in the following years.

Change may be hard, but at some point it will have been better to change it than to leave it as it is right now.

Not ideal but the closest we get after the destruction Swift 3 and 4 brought to it.

The initial model with folders as an additional step (aka submodule) would have been my personal ideal solution.

I think access control as a topic is still too hot (as in radioactive) to be openly discussed. At best, I think we can have a discussion about submodules or namespaces. I have an idea about namespacing, but I'm not sure that now is the time to really focus on it. Then again it might not hurt to discuss it.

As for being able to access things from submodules/namespaces without prefixing the name of said submodule or namespace; I think that sort of sugar can wait until we at least have an idea of how we want them to work in the first place.

Hi David, thank you for sharing that with us. Can you explain what kind of complexity you're talking about? To me there can be two kinds, implementation complexity and usability/learning complexity. I think the latter isn't really making anything more complex than it already is, but the former may indeed be very complex. Since I'm no compiler engineer I can only say how it 'feels' to me, and if I had to compare then conditional conformances had a mich higher complexity implementation-wise then access control through out the files. Sure spreading everything into files can lead to issues like SR-631 but it's an obvious thing that is allowed by the language design. Extensions also are not tied to a specific file and therefore shouldn't the access control be limited if it's non of the file prefixed modifier.

As I mentioned in the original post, I think that after the introduction of type + file bound private default, the naming of fileprivate started to make no sense. Right now private really should correspond to 'bound to a type' (in fact it is now bound to a type + current file), while the file prefix should limit the access to the current file. In that sense the current private is acting like a real file-private access modifier (bound to the type and current file), while the current fileprivate is something like file-internal access modifier (limiting the internal visibility to the current file) because it lifts up the visibility above private.

Regardless implementation complexity and the final spelling for a wish thinking of mine on how access control could look like, I think it won't really harm anyone if had the following control below public:

  • internal

  • internal that is limited to the current file only

    • Old name: fileprivate
    • Potential naming: (file)internal

vvvvvvvvvvv access control gap in-between vvvvvvvvvvv

  • private that is bound to the type in which it was declared only (no access for sub-classes)
    • Old name: -
    • Potential naming: private

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  • private that is limited to the current file only and the type in which it was declared
    • Old name: private
    • Potential naming: (file)private

I'm not trying to reinvent the wheel here or anything, because a lot has already been said. In this thread I'm just talking with you guys about the observations we all did in the past few years including the status quo. I really wish to fix the naming of fileprivate and also to close the access control gap in-between two existing modifiers.

This would create the most flexible access control for Swift and as a big advantage it would solve the issue of in-line unit tests from different files, because then you could access private members from different files, while (file)internal/private would be super rare anyway and those members could be tested in the same file.

The default and most common access modifier would remain:

  • open/public
  • internal
  • private

Rare access modifier:

  • (file)internal (historical reasons)
  • (file)private (source compatibility reasons)

As I said before I started this thread to discuss issues a lot of us are facing right now and I presented one possible solution to that problem. If you really don't understand the issue and are super convinced that we should carry a half-baked system around and just move on to the next big thing, then I suggest you let this thread cool down on it's on. If there is really no interest for this conversation it will fade on it's own anyways, therefore it's a discussion rather then a pitch thread. Furthermore if you think that sub-modules should be pushed instead, then I also highly encourage you to stand up on your feet and push that particular topic forward, because it won't do it on it's own nor is it by any means productive or constructive in this thread to decide and judge the success or failure of this topic on a potential future thing.


As I already mentioned couple of times before, I'm going into this thread with a cool mind and not expecting too much, except a nice and constructive discussion with people who can present facts and discuss pros and cons of the current system vs. modification that can be made to improve it. In that sense I'm not trying to trigger here anyone, and if you do feel like that, than I'm sorry for that.

Personally I do care about Swift a lot, since it became my favorite programming language. I'm also confident if anyone would share as much passion as I do we could finally start to agree on things more quickly and collaboratively work together instead of preventing the language to grow and be fixed where it's leaking.

4 Likes

But the access control isn’t half baked - it is just different then in Java/C++...

I like the idea you presented for access control below public and I appreciate your efforts to have a good discussion about this a lot. Reading such discussions can also be very helpful to understand the complexity and the details of a topic to people not jet used to it (like me).


I have some questions about concretely the ideas you presented above.

I guess thats a (module)private in your description (that's not explicitly specified or I didn't see it), not a "pure" type restricted access control solution, such that I could also access these private members from an outside module, if I am extending the type.


It looks like we were mixing two different access control systems here, restricting access based on modules and restricting access based on types. From my limited perspective, it seems like doing this isn't uncommon in other languages, but it might add unnecessary (learning) complexity.

To demonstrate what I want to say, I am going to describe the most extreme form of having these two access control principles mixed together, however I don't want to actually have this:

Theoretical description

Basically this means having type and "whole scope" access control on all levels, with these granularities:

  • type:
    • type (access restricted to a type)
    • global (access not restricted to a type (visible in the whole module/file/etc.))
  • scopes:
    • file
    • module
    • universe (accessible from everywhere)

Then if these could be arbitrarily combined, we would have

  • access-restriction(file, type), this is (file)private from this thread or the current private
  • access-restriction(file, global), this is (file)internal or fileprivate
  • access-restriction(module, type), this is the new default private in this thread, the missing access control level to separate some (somehow interconnected) functionality of one type into two files
  • access-restriction(module, global), this is the current internal
  • access-restriction(universe, type), this is another level that is similar to protected, but not the same because it has nothing to do with subclasses. Elements of a type declared this way will only be accessible in extensions of this type. This might be useful for sharing some utility methods also useful in other context, but allowing this might also be a non-goal
  • access-restriction(universe, global), this is the current public.

open isn't covered at all here. It could be added with a third "subclass" (which would mean overridable in subclass) case as additional type access view. If we allowed this case with all three scope cases, we would gain three additional combinations with open-like features for file and module visibility.

Since we have open and public, but no ability to express the same thing at the internal level, I would not know why we shouldn't have the type visibility only on the internal level (and the file level, where we already have it) in general, except that it clearly destroys the easy to understand subset relationship between access levels.

To keep the subset relationship between the access modifiers, we could move the new private one step up, above "fileinternal". private would then allow access from the file a type is declared in and in extensions of this type inside the same module.


I think both (file)internal and (file)private will still have use cases for people that like to pack multiple types inside one huge file because they like to do this, like I like not to do this, but spread one type through multiple files. The current access control system does indeed provide more functionality for the people putting everything inside one file. Similarly it isn't possible to define new overridable methods in an extension of a class. Certainly there is a reason for this (and I would really like to know it!).


In my opinion we are missing a feature to express an access restriction to extensions of a type inside a module, because it is possible to split functionality of a single type into multiple files with extensions.
As far as I know, this isn't a misuse of extensions, but a valid use case. Whether it is a good idea to organise things into multiple files or not is another thing and actually something every team or programmer has to decide on it's own for it's own code. Swift makes both possible.

1 Like

Yes but the problem only exists because people insist of splitting tightly coupled code into several files instead of keeping them just together.

So I would not say Swift has a problem, but part of the users who insist of working against the language instead of with the language.

If the current type system is on purpose designed to give a guideline what is a good style to put inside multiple files and what not, this discussion is already settled. However, I don't think the current type system was designed with this purpose in mind (If I am wrong here, could you please give me a link to the proposal that described the last change?).

I think we should accept that both organisational approaches are just both ok. I don't know any effort inside swifts development that was explicitly meant to strike against one of these approaches.

Again, I think this is a side effect of the access control system, not it's intent. Current private allows restriction of access to a type (and extensions of it) on file level. I think the main reason that this wasn't allowed at module level was the implementational complexity.

1 Like

No the access control was intentionally bound to files not to types. The core team more then once said this. That's why many are annoyed by all the repeated tries to change that.

2 Likes

So why is private tied to types?

Allright, I'm gone do some research about this to educate myself. @anon27714269 would you mind giving me the links where I can find these statements, to give me a starting point?

Its the fallout from the last try to change it that lead to the don't touch it in the foreseeable future status quo.

That's hard as the discussion was initially in the apple developer forums and these are very bad to search in. After all the access control is older then the open sourcing of Swift. But I will try to find it.

Can you provide links from archives to strengthen your statement please? I think this would be helpful in this thread in general.

Furthermore I'd really appreciate if you also can elaborate on what disadvantages type bound access control for private would bring to Swift, to it's community and all the possible paradigms out there that Swift does support and how in comparison file bound access control is supposed to be better, regardless implementation complexity.

In this thread we're not in the rush, since it's supposed to be a discussion thread rather then a ptich thread (I already mentioned that above), so we can work together and summarize instead of just saying, someone said something at some point in the past and just leave it be.

1 Like

If the old discussions are hard to find, it would be very helpful to have the reasons here, for now and for future discussions.