Calling private methods from extensions in separate files

I'd be quite happy if extensions to a type within the module that defines the type could access private members of that type. Essentially turn private into the proposed typeprivate. Given the current alternative of forcing members to be internal, I don't see any downsides.

1 Like

There's nothing wrong with (file)private functions and members used for implementing the actual interface of a type. I just believe that if you can't fit all of those in one file, then there's something wrong with your code.

That is way too judgmental and subjective to be a valid argument, even if it is a valid opinion.

1 Like

In certain cases, Scoped Conformances could help, which seems to be likely implemented in the near future.

For other cases, it would be nice to have something between internal and fileprivate. I think I was there that neither a new module, nor internal access modifier felt appropriate.

But I can live without it. Scoped conformances however I really desire for!

We split code based on module responsibility, not teams or origin (third party or not). It works great and feels like internal is used as intended. We sometime implement protocols in an extension of types from other modules, but those should not have access to the internal members anyway, so it's a plus, keeping things tidy. And if they do, then it usually means that there's a strong overlap between modules, which usually signals a flaw in the architecture (i.e. responsibilities are not handled correctly, maybe a third module is required, etc)

Yeah, but it does unnecessarily bloats the language. Just because we can, it doesn't mean we should. Also, I think that implementing protocols or in general, anything in an extension should be seen as a way to add behaviour using the type's public interface, without manipulating the internal state.

Well, let's explore the idea space for a moment.

For the most part, the difference between foo(a, b) and a.foo(b) is virtually non-existence, to the point that we have "Prefer methods and properties to free functions." to Swift.org - API Design Guidelines, expecting us to be able to choose either form on the fly. We also want something between fileprivate and interval, which is the main portion of this thread. Finally, I want to throw in an extra that one would normally move the files around for refactoring, so it should support that too.

Putting the requirements together, the feature should

  1. Work well for both a.foo(b) and foo(a, b) forms,
  2. Be between fileprivate and internal,
  3. Require minimal changes when moving files around.

Now, access control normally means you can use X so long as you have the same Y. For fileprivate and internal, Ys are "the containing file" and "containing module", respectively.

For typeprivate idea, Y is "the type that owns the function". So they would fail (1) but pass the other two.

Another idea is to have folder level access control, which would fail (3) but pass (1) and (2).

Finally, since neither typeprivate nor folder exactly fits the bill, maybe we can create our own Y. Let's name them, IDK, namespace, which can be declared as needed. The idea is that you declare a namespace somewhere in the module, then use it to identify the access control level (between private and internal) as needed. When you need to use anything from inside the namespace, you can then import it (parallel the idea that namespace is a mini-module).

// A.swift
namespace Namespace1

struct X {
  protected(Namespace1) func foo() { .. }
}
protected(Namespace1) func foo() { .. }

// B.swift
private func bar() { foo() /* fail */ }

// C.swift
import namespace Namespace1
private func bar() { foo() /* ok */ }

This would pass all the requirements, and should pretty much work for your problem as well.


I can never appreciate the "opt-in" argument. For sure, I can just opt out of the feature on my own, but what if I am part of a bigger team? I guess I can negotiate. What if I want to contribute to an opensource that opts in? What if someone wants to contribute to my open-source project and wants to opt-in? This argument only works when you live in a vacuum. We don't. I either leave or stay with the codebase that uses it, much to my own detriments.

Furthermore, it's dismissive and very disengaging. It says at least as much as

  • "I agree that it's a problem, but"
  • "I'm not going to do anything about it."

If it's not that big of a problem, convince me. If it can be alleviated, please do so. Such an answer doesn't move the conversation forward and simply leaves me out of the equation.

You could even say that I can still work around that by bypassing the type on my own. It's much easier for Swift as you can add any function to any type:

struct X {
  func bypass() { X.typeprivateFunction() }
}

func foo() { X.typeprivateFunction() /* bad */ }
func bar() { X.bypass() /* ok */ }

This is not ideal, but at the very least doesn't bar me from having the API I want. Even I, who is a skeptic of typeprivate, can come up with that much. I'd expect at least this much, or at least, anything better than "Just don't use it".


Now, I'm not claiming the ideas to be good or anything (and folder is just an old idea others have pitched). Both folder and namespace ideas would also have implementation hurdles to overcome, and one can reasonably come up with entirely different sets of requirements from me. That said, at the very least, I really wish we would at least take a deeper look before both sides start being defensive of their own champions.


I'll probably go and cool off from this thread a little bit. I came in expecting some new stuff on the table given there are a few new faces here, but it seems to end up much the same.

1 Like

This is inaccurate for private, which is a hybrid.

private in Swift actually means fileprotected. I.e. the same as protected, but invisible to other files.

And fileprivate would be better expressed as fileinternal.

1 Like

I will review your folder/namespace ideas later on. A quick meta comment now about the above. I can well appreciate yout point. At the end of the day either one camp is coerced to the other camp's preference or there is a language fragmentation, either not ideal. The "don't use access levels if you don't want them" argument is probably a semi-emotional response to the other camp's claims that "1. there is no problem to solve". and "2. if there is a problem just use the heavy artillery modules".

1 Like

One thing I have mentioned previously in terms of finer grained access control is for Swift to clone and modify Rust's module system as "small modules", with a new access control level external.

That is add a mod concept which in some manner groups files together in a new unit.
All old "large" modules contain a single implicit "small" module.
New "large" modules can contain several "small" modules

This means changing the meaning of internal to be within the smallest containing mod is not source breaking (I think), thus external could be a new access control level which is either equivalent to the old internal (all files in the "large Module") or just all files in the/any parent mod.