Here’s an early draft of the proposal. Please fire away with comments! By the way, how would the visibility in subclasses in the same file be handled?
https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md
Type-based Private Access Level
Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md>
Authors: David Hart <http://github.com/hartbit>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#introduction>Introduction
This proposal extends the visibility of the private access level to all extensions, declarations and nested types in the same file.
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#motivation>Motivation
Proposal SE-0025 <https://github.com/hartbit/swift-evolution/blob/type-private/proposals/0025-scoped-access-level.md> introduced a lexically-scoped access level named private and renamed the file-based access level to fileprivate. The hope was for private to be a good default for restricting visibility further than the whole module and for fileprivate to be used in rarer occasions when that restriction needed to be loosened.
Unfortunately, that goal of the proposal has not been realized: Swift's reliance on extensions as a grouping and conformance mechanism has made fileprivate more necessary than expected and has caused more friction between private and fileprivate than intended.
As a consequence, experience with using Swift 3 has caused mixed reactions from the mailing list and greater Swift community, culminating in proposal SE-0159 <https://github.com/hartbit/swift-evolution/blob/type-private/proposals/0159-fix-private-access-levels.md> which suggested reverting the access level changes. That proposal was rejected to continue supporting the code written since Swift 3 which benefits from the distinction between private and fileprivate, but it was recognized that the language's lack of a good default access level more restictive than internal was unfortunate.
In the hopes of fulfilling the initial goal of SE-0025 <https://github.com/hartbit/swift-evolution/blob/type-private/proposals/0025-scoped-access-level.md>, this proposal defines the visibility of the private access level to extensions, declarations and nested types of the member's type in the same file to increase its usefulness and as a good default private access level. fileprivate then logically achieves its intended goal of being more rarely used and adheres to Swift's "progressive disclosure” philosophy.
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#detailed-design>Detailed design
The design of this proposal defines the visibility of a private member declared within a type X or an extension of type X to:
the declaration of X if it occurs in the same file
all extensions of X or subclasses of X in the same file
all declarations of nested types of X in the same file
all extensions of nested types of X in the same file
To illustrate the consequence of those rules, the following examples will be used with two files in the same module:
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#personswift>Person\.swift
struct Person {
let name: String
let gender: Gender
private let age: String
var greeting {
return "Hello, my name is \(name)"
}
init(name: String, gender: Gender, age: String) {
self.name = name
self.age = age
}
func greet() {
// age is accessible because it is defined in the same declaration but secreyAge is not because it is defined
// in a nested type so the following piece of code would generate a compilation error:
// if age < gender.secrecyAge {
// instead:
if Gender.shouldRevealAge(self) {
// fullGreeting is accessible because it is defined in an
// extension of the same type in the same file
print(fullGreeting)
} else {
print(greeting)
}
}
}
// private at the top-level scope continues to be equivalent to fileprivate
private extension Person {
// fullGreeting is implictly private due to the extension's modifier
var fullGreeting: String {
// age is accessible because it is defined in the declaration of the extension's type in the same file
return "Hello, my name is \(name), I'm \(age) years old."
}
}
extension Person {
enum Gender {
case male
case female
private var secrecyAge {
switch self {
case .male: return 60
case .female: return 50
}
}
static func shouldRevealAge(_ person: Person) -> Bool {
// age is accessible because we are in the declaration of a nested type that declared age
return person.age < person.gender.secrecyAge
}
}
}
extension Gender {
static func leakAge(of person: Person) {
// age is accessible because we are in the extension of a nested type in the same file
return person.age
}
}
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#otherswift>Other\.swift
extension Person : CustomStringConvertible {
var desription: String {
return fullGreeting
// error: fullGreeting is not available because it is defined in another file
}
}
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#source-compatibility>Source compatibility
Incoming...
<https://github.com/hartbit/swift-evolution/blob/type-private/proposals/XXXX-typed-based-private.md#alternatives-considered>Alternatives Considered
Incoming...
···
On 3 Apr 2017, at 20:34, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift Community,
In rejecting SE-0159 <https://github.com/apple/swift-evolution/blob/master/proposals/0159-fix-private-access-levels.md>, the core team described a potential direction we would like to investigate for “private” access control that admits a limited form of type-based access control within files. The core team is seeking some discussion here and a motivated volunteer to put together a proposal along these lines for review in the Swift 4 time-frame (i.e., very soon). To be clear, the core team it’s sure this is the right direction to go… but it appears promising and we would *love* to be able to settle the access-control issue.
The design, specifically, is that a “private” member declared within a type “X” or an extension thereof would be accessible from:
* An extension of “X” in the same file
* The definition of “X”, if it occurs in the same file
* A nested type (or extension thereof) of one of the above that occurs in the same file
This design has a number of apparent benefits:
+ “private” becomes the right default for “less than whole module” visibility, and aligns well with Swift coding style that divides a type’s definition into a number of extensions.
+ “fileprivate” remains for existing use cases, but now it’s use it more rare, which has several advantages:
+ It fits well with the "progressive disclosure” philosophy behind Swift: you can use public/internal/private for a while before encountering and having to learn about “fileprivate” (note: we thought this was going to be true of SE-0025 <https://github.com/apple/swift-evolution/blob/master/proposals/0025-scoped-access-level.md>, but we were clearly wrong)
+ When “fileprivate” occurs, it means there’s some interesting coupling between different types in the same file. That makes fileprivate a useful alert to the reader rather than, potentially, something that we routinely use and overlook so that we can separate implementations into extensions.
+ “private” is more closely aligned with other programming languages that use type-based access control, which can help programmers just coming to Swift. When they reach for “private”, they’re likely to get something similar to what they expect—with a little Swift twist due to Swift’s heavy use of extensions.
+ Loosening the access restrictions on “private” is unlikely to break existing code.
There are likely some drawbacks:
- Developers using patterns that depend on the existing lexically-scoped access control of “private” may find this new interpretation of “private” to be insufficiently strict
- Swift’s access control would go from “entirely lexical” to “partly lexical and partly type-based”, which can be viewed as being more complicated
Thoughts? Volunteer?
- Doug
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution