Access modifiers #725
Replies: 0 comments 7 replies
-
I've been a long proponent of the idea that Swift changing the definition of private was a mistake. I am not convinced that anything more granular than public, internal (the default), and private (i.e. file private) are needed. Especially in a language with actual namespaces. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
After discussing with @dabrahams, we think we could start with only // In A.val
public type A {
var raw_value: Int
public memberwise init
}
fun foo(a: A) -> Int {
a.raw_value.copy() // error: `A.raw_value` is private
}
// In B.val
extension A {
public fun bar() -> Int {
raw_value.copy() // OK
}
} Assuming |
Beta Was this translation helpful? Give feedback.
-
I'm opening the discussion about the design of Val's access modifiers. In my experience, access modifiers is a common topic of disagreement so I'm ready for very strong opinions ;)
Motivation
Access modifiers are an important component of API design. However, traditional approaches tend to be not very flexible and particularly not well-suited to extensions and post-hoc conformance declarations. Even Swift's design has some wrinkles.
Swift has 5 access modifiers (from least to most restrictive):
open
(only relevant to Swift's class system)public
internal
(default)fileprivate
private
Despite these 5 modifiers, it is not possible to declare an extension that is able to access private members and expose new public entities. Since Swift uses extensions to declare post-hoc conformance, private details of a type must sometimes be exposed more than necessary just to support a conditional conformance declaration (unless the extension is in the same file).
Further, it is also not possible to allow a companion type to access private details of another type. Although this limitation can be mitigated using
fileprivate
and/or some clever nesting, it remains a hurdle in general.Proposed approach
We define the following access modifiers:
public
internal
scoped(to: S1, ..., Sn)
private
(default)Note:
internal
is an alias forscoped(to: M)
whereM
is the name of the enclosing module.public
means exposed outside of the enclosing scope. Apublic
declaration is part of a module's API if and only if all its enclosing scopes are public. For example, in the file below,A
andA.f
are part of the module's API whereasA.g
,B
andB.f
aren't.scoped(to: S1, ..., S2)
can be used to expose a declaration nested in a scope without making it as visible as that scope. For example, in the file below,A.g
is visible to the whole module but, unlikeA.f
, it is not part of that module's API.scoped(to: S1, ..., S2)
can also be used to confer access to a companion scope. For example, in the file below,A.f
is only visible withinA
,B
, andC.X
.Note that a declaration that is
scoped(to: S)
is also visible in the extensions ofS
.private
means only visible within the enclosing scope and is the default access level. One exception is made for extension and conformance declarations in the same module, which are allowed to access the private members of the type they extend.Note: This exception is more permissive than Swift, where it only applies to extensions in the same file.
private
applied to top-level declarations of a file make them visible only to that file. Onlypublic
andinternal
top-level declarations are visible to the whole module.Rationale
I originally thought we could get away with only one access modifier:
public
. A declaration would be private by default andpublic
would simply make it visible to enclosing scope. If this scope is alsopublic
, then the nested declaration is also visible to its enclosing scope, etc. For example:This approach gets you surprisingly far, but it has a couple of annoying limitations. First, note that members of declarations that are part of a module API can't have members that are only visible within the module. Second, it is not possible to access private declarations in extensions.
For example:
@dabrahams suggested a very nice feature that possibly addresses both issues.
This modifier is reminiscent of C++'s
friend
feature. I think that if we combine it with namespaces, then we can work around the two issues I mentioned.Alternative considered
Rather than allowing all extension and conformance declarations access to the private declarations of the extended entity, we could add a
protected
modifier that confers and makeprivate
stricter. However, the benefits of this feature is questionable.Beta Was this translation helpful? Give feedback.
All reactions