Access control below public [not `protected`]

Thanks for your explanation, this was what I was missing.

Indeed, this is a possible solution, that has many advantages, but I also see some (more) disadvantages.

If this feature set is used heavily to structure code, then the expessiveness of the access control modifiers gets lost.
When I declare something public at the third nesting stage, it is not at all clear what this means.

This is already true for access control today, where the access control level of the type sets a maximum, so probably needs to be solved at the tooling level.

internal struct InternalStruct {
    public var i = 0
}

private struct PrivateStruct {
    public var i = 0
    internal var j = 0
}

In the end I think most people won't use submodules heavily (and particularly not nested submodules), so I don't see this as a big problem, and there are planned tools to help with library evolution that may also help here.

Edit: And I don't see nested submodules as definitely necessary here, or know if they would be a great idea. Perhaps all submodules would be at top level, and the rest of my post would still stand. This limits how baroque people's modularisation can get, which you may consider a good or bad thing.

Apologies, I was unclear at the end of my last post. I wrote 'inheritance' but I still meant 'type'. They're equivalent to me in this discussion, though I am more against inheritance coming into play than type. I am still bothered by the current involvement of type in private.

1 Like

This actually makes a lot of sense. In an extension-heavy language like Swift, where also inheritance is discouraged, the current implementation of private seems unfitting.

The private we have today makes sense when your types are file-bound. If we're gonna work with extensions and w/o inheritance, private as it stands today loses a lot of its merit. We should have something like what you suggest: a type-bound private.

1 Like

Only if you insist on splitting extension into separate files. I'm still not convinced that is better then keeping such tightly coupled code in the same file.

3 Likes

It's probably worse for some use cases and better for others.

The problem with the current scheme is that it allows only one scenario, not both. What DevAndArtist is proposing would allow both. Maybe not same-file work as optimally as it is today, but it would still allow it to be performed well while allowing different-file work done well also.

EDIT: I'm sorry, the current scheme actually has a lot of problems. This is just one of them.

Well I'm a big fan of "there is only one way to do it" - Where "anything goes" leads to is visibly at one of the worst languages ever written: C++, I don't want that to happen to Swift.

Well, but is that why C++ is bad? I think that's not the reason why C++ is bad.

There are two important reasons (that I know of) why C++ is bad:

  • You turn a corner and find a "Gotcha!". The language is not beginner-friendly by a loooooong shot.
  • Pointers. So many pointers.

That fix does not transform the language into C++, if you don't want to write code in different files you still don't have to and can use the pitched private the same way you're using private today. It does not break your code nor will it create a disadvantage for you. The whole time I only was writing about that particular situation that Swift only prioritizes one half of the developers which is a bad thing in a style-agnostic language like Swift. No protected just a more flexible private default, while we'd also keep the current version of private arround as something like (file)private for source compatibility reasons and align fileprivate into (file)internal. Such design would make it clear that you limit the visibility to the type, type + file or the file (the latter is (file)internal).

1 Like

I understand what you want and will fight it till the end. Access control should never be bound to types.

It's clear that some people want to be able to provide extensions in separate files that have access to "private" members. The question I have is
 why is internal not an acceptable access control level for such members? Allowing extensions in other files access to private members would basically be equivalent to internal anyway, with just a bit more typing.

// --------------------------------- //
// FileA.swift
struct CartesianPoint {
    private var x: Int
    private var y: Int
}

// --------------------------------- //
// FileB.swift
func test(point: CartesianPoint) {
    print(point.x) // This doesn't work
}

// If extensions in other files can access private things,
// we can do this:
private extension CartesianPoint {
    var sneakyX: Int {
        get { return self.x }
        set { self.x = newValue }
    }
}

func test2(point: CartesianPoint) {
    // Now I have access to supposedly "private" implementation 
    // details of CartesianPoint! I'm so sneaky
    print(point.sneakyX)
}

Allowing extensions access to private members basically means that private no longer provides any protection from use anywhere else in the module. Someone reading the original declaration that says private can no longer trust that access is limited to this particular file; now you have to search the entire module for any uses. This means that anywhere you see private, you would have to code as if it could possible treated as internal.

If you actually want access from anywhere in the module, just declaring it as internal would seem like a better description of the behavior to me.

7 Likes

Hi @bjhomer I think you misunderstand the actual use case. internal would expose private API to other distinct types that should not have access to that API while private should keep the access only bound to that particular type where the private member is declared. If for instance you have multiple complicated caching algorithms that requires some file private helpers you may not want to throw everything into one bucket and create a giant file but rather seperate them into a few files that are tied together while you don't want to expose those algorithms outside that particular type. If you make them internal then anyone can use those and you can have potential API misuse, while a type bound private would prevent that and allow you organize your code more nicely.

1 Like

Yes, but as I demonstrated, it's trivially easy to add an extension that then re-exposes your private details to other non-related types.

3 Likes

I do think it could be nice to have some mechanism for treating multiple distinct files as "related" in some way. This was also brought up in a discussion of being able to test private API implementations. But I don't think a separate access control level is the right way to go.

That example is contradictory, you can do exactly the same in the declaration file. To me this isn't a valid argument against allowing a type bound private.

struct CartesianPoint {
    private var x: Int
    private var y: Int
}

extension CartesianPoint {
    private var sneakyX: Int {
        get { return self.x }
        set { self.x = newValue }
    }
}

func test2(point: CartesianPoint) {
    print(point.sneakyX)
}

Wouldn't that be easily solved by implementing submodules?

Yes, you can do exactly the same thing in the original file. But currently, when I write private var or private func, I know that I only need to audit usages from within this file. That is precisely the value that declaring it private gives me; it limits the scope of potential misuses I need to audit.

Allowing any file to access private values via an extension shim means that I now have to audit the entire module.

3 Likes

Ahm, access control isn't there to protect you from the need to think over what you write yourself...

1 Like

Please leave sub-modules out of this thread, if you wish Swift to support sub-modules ASAP I highly encourage you to open a new Thread (your first ever) and start contributing to the evolution process. You have stated your position crystal clear in this thread by this point of time, everything else is just unnecessary noise as @Avi clearly noted. I don't want to see any flames in this thread, so I ask you kindly to stop repeating yourself.

1 Like

That I won't - As I said, I will fight this idea till the end. It is not good for Swift. You will have to live with it.

2 Likes