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
- Work well for both
a.foo(b)
andfoo(a, b)
forms, - Be between
fileprivate
andinternal
, - 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
, Y
s 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.