[swift-evolution] Type-based ‘private’ access within a file

David Hart david at hartbit.com
Mon Apr 3 16:14:36 CDT 2017


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 <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 at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170403/493d94bb/attachment.html>


More information about the swift-evolution mailing list