I am aware of the fact that there has been a lot of discussion related to this topic. However, such discussions always quickly change into debates related to things like "whether fileprivate is a good idea", "differences between lexical access level and type-based access level" or "whether it is easy to understand and learn". And it seems that it has been quite a long time without any new discussions related to this topic, I'd like to talk about this requirement and possible proposals again.
Motivation
Some developers (including me) like to group related things in different files, especially when we are implementing multiple protocol conformance for a type, to prevent a file from becoming too long. Actually even Apple themselves are doing this. Just check the source codes of swift-foundation, they have Decimal.swift
and Decimal+Math.swift
. Such implementation may require accessing properties or other methods in that type. Since we are in another file, those members have to be declared as internal
. Of course using internal is fine, but it causes the code-completion to present some garbage that we may never directly access when we are using the type in other places. Some developers may design the name of such member to start with _
(again, swift-foundation is doing this, such as Decimal._length
), but it still looks not elegant enough. (similar goal as partial class
in C# ? )
Proposed Solution
Note that this solution is not really something new since many people has already proposed similar things (such as the typeprivate modifier introduced by @userFortyTwo). The main goal of this post is to further clarify the motivation and talk about some common misunderstandings, but still I will put the proposed solution here just to make the post looks complete.
Introduce a new modifier extensionprivate
that allow the member to be accessible from extensions in the same module
// in file1.swift
struct SomeType {
extensionprivate var someProperty: String
}
// in file2.swift
extension SomeType: CustomStringConvertible {
var description: String { "SomeType(\(someProperty))" } // this is ok
}
// in file3.swift
func example() {
SomeType(someProperty: "").someProperty // this is an error
}
This new access control modifier offers the same protection as internal
:
- can be accessed anywhere in the same declaration body
- can be accessed anywhere in the same file
- can be accessed in extensions in any files in the same module
- CANNOT be access from another module
Some Common Misunderstanding
- This new modifier has nothing to do with
private
andfileprivate
, in other word, it does NOT mean accessing private members. If you are sure that a member should never be accessed from the other file even in extensions, just keep usingprivate
andfileprivate
. If you want the member to be accessible anywhere in the same module, just keep usinginternal
. My point is that this new modifier does not break the behaviour of any existed modifiers, so even if you don't want to use it and prefer to write everything in the same file, it won't stop you from doing that. - It is true that this modifier can be easily bypassed by defining an
internal
or evenpublic
function in an extension in the same module and expose it. However, as I have mentioned before, this modifier provide the same protection level asinternal
, so you are not really bypassing anything, this is just something expected. Besides, if some "bad guys" is able to write extensions for your type and access internal level members, they already have write access to your source codes, then they can simply write public functions in the same file of the type to expose all yourprivate
members. So such concern makes no sense to me. The aim of this modifier is to allow easily writing extensions in multiple files without having to declare members asinternal
and see "garbage" in code completion. - Many people proposed introducing submodules to Swift. It might also be a good solution, but it requires additional configurations or definitions, and we have to write more
import
statement, which can be a little annoying. - There are also some complains about the increased complexity, which make it harder to understand. Well, since
extensionprivate
has the same naming format asfileprivate
and it actually has the same protection level asinternal
, it should not be too hard to understand, right?
Further Consideration
There is still one thing of this new modifier that needs further discussion: should a member declared as extensionprivate
be directly accessible from its sub classes? Personally I will vote for yes since subclassing is also a form of extending a type?