Access control below public [not `protected`]

Dear Swift community and core team, please don't judge me too much or scream at me just because I want to talk about this particular topic with you. I hope we can have a nice, constructive and democratic conversation here. I'm not a compile engineer, so my words can be wrong or wrongly interpreted since I'm not eloquent nor am I a native English speaker.

The current status quo of the access control below public is that we wanted to find a better default for private last year to fade out fileprivate, by making private a little bit more like fileprivate but limiting it to the type. The Swift community - from my point of view - is sliced in two halfs. One half moves a lot of type related computation and type internal API into extensions and different files, the other half may not care about such code organizations and stack everything into one giant file. For some reason the access control seems to prioritize the second half and is still completely inflexible for the first half of Swift developers. If you take a deep breath and think for a second then you may start to realize that fileprivate has lost it's weight, at least from the naming perspective. fileprivate really is not an access modifier that allows access to private type members but an access modifier that grants visibility to the type member only in the current file. If we put that with different words then it becomes a file internal access modifier.

I speak here for myself, and only for myself, but I think that probably everyone from the mentioned first half wished to gain access inside a type extension, inside the same module to the private members. But for some reasons this access level does not exist in Swift, therefore it feels like the current access control does prioritize only the second half of the Swift developers that do not organize their code in different files. You may argue that we can document members that require this access control to warn our colleagues from using it, but this does not solve the issue nor does it prevent it. I think it's fair to say that access control was invented for a specific reason, not one that should rely on documentations.

To sum up the issue below public access control:

  • Last year we tried hard to find a better default for private to fade away fileprivate, which kinda worked.
  • The new default limits the access to the current file and keeps it private to the type (in fact, it became the true (file)private access control).
  • The fileprivate stats to fade away but has completely lost it's weight from the naming perspective.
  • [I think] We can migrate the access control in a rotational manner to find an ever better and far more flexible default for private.
  • [I think] We should deprecate fileprivate.

I'm not directly proposing this change but would love do discuss it here and see where it goes.

  • Deprecate fileprivate and introduce a new kind on how to express an access modifier by prefixing a specific set of exiting access modifiers with parentheses with a special keyword in-between: (keyword)access_modifer

  • Migrate fileprivate to (file)internal.

  • Migrate private to (file)private (this is necessary for safety migration to prevent possible collisions with the new default for private).

  • Introduce the new default private which would allow to access that particular type member from inside any file inside an extension of the same type where that member was declared. As mentioned before to prevent possible collisions we would need to migrate current private to (file)private and then everyone can manually remove (file) where no collisions are possible or resolve the collision before removing (file) prefix from before private.


Another argument against this is to introduce sub-modules and let them handle the mentioned access control issue, but this only forces you to create dozens of small sub-modules to compensate the main issue by lifting the access control one level up which will resolve to even more boilerplate code.

I might have forgot to write something while I tired to describe the issue in an understandable way from a non-compiler engineer who's lacking some compiler jargon.

4 Likes

Putting a module into a single file is imo the right thing to do. Don't know where this littering the project with hundreds of little files started.

I'm strongly opposed to break away from file based access control.

1 Like

One example from my personal habit is that I put extensions for *DataSource protocols into a different file MyType+*DataSoruce.swift, but in most cases the methods from the *DataSource protocol would require access to private type members, which forces me to upgrade those to internal and prefix them with an underscore to signal a 'hands off implementation detail due to access control limitations'. That's just for one type. If you then have multiple similar types which should not have such visibility across them you'd need submodule per type, which as I mentioned in the original post is not solving the main issue but rather creates a new one with even more boilerplate code and potentially even worth code organization.

But again this is only my personal point of view since the discussions about sub-modules have not presented a single solution to this problem.

1 Like

If they are so deeply coupled they should go into the same file.

I'm not judging, but this is exact the situation that slices the community into two halfs. It's definitely possible to solve the issue like that, but why should we be forced to create huge swift files?

1 Like

Why should we endure the 1000th try to get PROTECTED into Swift that was denied a 1000 times already?

1 Like

I understand where you're coming from with this request and I have some degree of sympathy. But IMO I think the access system is pretty much "fine" the way it is now. I say fine because I preferred the old system before fileprivate. It was simple and got the job done for me.

This isn't what I would call "private", this is what I would call protected. And while you mention only extensions, it begs the question about subclasses. Which is where most of the opposition I've seen comes from. Swift's access control system explicitly rejects OOP notions of access.

6 Likes

I am quite confident we will not be changing the behavior of private—we had a major debate about access levels last year, and after extensive input from all sides the core team decided on the current meaning. Let’s leave that part alone.

I agree that fileprivate is an unfortunate spelling, and it would be great if we could find a better name for it. Furthermore, I also agree that when we get submodules as a way to group related files then we will want an access level for that. Repurposing fileprivate into “submodule-internal” (with a nicer spelling :-) seems feasible and reasonable.

I am not convinced we need further parameterization of access levels. Submodules should solve most of the same problems, with larger code-organization benefits.

4 Likes

Pinkamena Jan Neumueller
May 5
Putting a module into a single file is imo the right thing to do. Don’t know where this littering the project with hundreds of little files started.

Overzealous SOLID fanatics that see methods longer than 5-8 lines as evil and responsibility divided between tons and tons of classes each doing a minuscule task? ;)

1 Like

That is a fair question. I'm not calling out to add protected the way it is in different languages, but rather upgrading private a little and fix the naming of fileprivate while keeping the same access control around by rotating the names a little and the way they're expressed. Since it would remain private it will still only be accessable in the same type only but not in any sub-class.

I still don't get where the problem with keeping all extensions in the same file lies. The are closely coupled so it's the natural way to store them anyway.

From what I remember sub-modules would require an optional name which if you do want to hide specific type members and still keep things organized will force you to come up with new (throw away) names. At least that is how I imagine this particular issue would be solved with sub-modules.

I do not understand what you are saying, could you give an example?

Okay sorry if I confused you. To begin with a sub-module requires a unique name just like a module does. That name must remain optional so that the user is not forced to write SubModule.OurType every time but rather can use OurType if it's not ambiguous. If you need a sub-module only to resolve the main issue I tried to describe in this thread, then you will have to come up with throw away names for such a sub-module. Furthermore I don't see the weight to use a sub-module and the extra boilerplate per type to workaround this access control issue.

A simple way to implement submodules is by just treating folders as submodules. The foldername becomes the submodulename and we just need a folder/submodule private. Easy peasy.

4 Likes

You can put it that way, but if sub-modules were that easy then I'm confused why we had such long discussions about them and they're still not implemented. That said, I'm very sceptical that it's just easy as that, regardless the implementation resources.


I'm not against sub-modules or anything, I'm just trying to fix issues in the current system before moving into something new and keep a bag of old issues around for historical reasons.

1 Like

It's people who hate filesystem based access levels who block this easy solution. We could have had this in Swift 3, but solutions have to be complex for parts of the community instead of keeping them simple.

I'm not blaming anyone but to me the same argument can be made against not allowing a little bit more flexibility in the general access control to folks that care about organizing their code into different files, because as you said it before "if something is coupled together it belongs into the same file" is then simply not true.

But this is exactly why I want to have a constructive and a cool discussion here. Last year we didn't have a forum and the whole discussion was spread around dozens of similar mail-threads, so let's use the luxury now and summarize things for once and for all.

1 Like

the feature you are asking to remove was the main design of se-0025. a revert proposal was rejected

there is no new information or new solutions in this post. the only possible way forward is some type of rust style pub mod but it seems this solution is not something we should discuss?

we tried, we failed. lets learn from it. lets heal and move on.

unless we have new ideas, i dont think rehashing old ones is beneficial, i think its better to try to fry bigger fish.

there are other issues in swift:

  • no great windows support
  • implicit static dispatch of extension (not being able to choose which protocol implementation to dispatch explicitly)
  • implicit message dispatch for @obj even in extensions
  • magic sub-typing of optional (its optionals??????? all the way down)
  • closure sub-typing. when does this happen? throws, escaping,
  • cant add extensions to func or tuples
  • reference cycles are still possible
  • no asyn/await yet
  • no yield
  • foundation library not 100% portable
  • no portable Playgrounds/Notebooks
4 Likes

For my defense, I wasn't asking the same thing as the rejected proposal, nor does this discussion goes against SE-0025 because the design from SE-0025 for private is no longer valid. All I was trying to achieve is to have a discussion about a small lift on how the default private can be to gain a greater range of access, fix an in-between gap of access control by preventing OOP related issues like questioned above and keep source compatibility without breaking anything.

1 Like