[Proposal] Revising access modifiers on extensions

Originally I started this topic in a discussion thread but moved it now into its own proposal thread due the lack of feedback.

This proposal contains a breaking change!

You can read a formatted version here: https://gist.github.com/DevAndArtist/8f1113b6d5d0379ebf82bd227cf4a88d

Feel free to also provide feedback if you spot any typos or language mistakes I might have made.

Revising access modifiers on extensions

Proposal: SE-NNNN
Author: Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

One great goal for Swift 3 is to sort out any source breaking language changes. This proposal aims to fix access modifier inconsistency on extensions compared to other scope declarations types.

Swift-evolution thread: [Proposal] Revising access modifiers on extensions

Motivation

When declaring members on extensions which don’t have an explicit access modifier in Swift 2.2, it is possible to create an implicitly public extension by applying a public modifier to at least one extension member.

public struct A { … }

/* implicitly public */ extension A {
     
    public var member1: SomeType { … }
    /* implicitly internal */ func member2() { … }
}

/* implicitly internal */ extension A {
     
    /* implicitly internal */ var member3: SomeType { … }
}
Furthermore in Swift 2.2 it is not allowed to apply an access modifier on extensions when a type inheritance clause is present:

public protocol B { … }

// 'public' modifier cannot be used with
// extensions that declare protocol conformances
public extension A : B { … }
Proposed solution

Allow access modifier on extension when a type inheritance clause is present.

Remove the behavior of an implicit public extension.

This changes should make access modifier on extensions consistent to classes, structs and enums (and SE–0025).

The current grammar will not change:

extension-declaration → access-level-modifieropt extension type-identifier type-inheritance-clauseopt extension-body

extension-declaration → access-level-modifieropt extension type-identifier requirement-clause extension-body

extension-body → { declarationsopt }

Iff the access-level-modifier is not present, the access modifier on extensions should always be implicitly internal.

Public Api:

Current version:

//===— Implementation version —===//

public protocol Y {
     
    func member()
}

public struct X { … }

/* implicitly public */ extension X : Y {
     
    public func member() { … }
     
    /* implicitly internal */ func anotherMember() { … }
}

//===— Imported modele version —===//

public protocol Y {
     
    func member()
}

public struct X { … }

public extension X : Y {
     
    public func member() { … }
}
New Version:

//===— Implementation version —===//

public extension X : Y {
     
    public func member() { … }
     
    /* implicitly internal */ func anotherMember() { … }
}

//===— Imported modele version —===//

public extension X : Y {
     
    public func member() { … }
}
Impact on existing code

This is a source-breaking change that can be automated by a migrator, by simply scanning the extension-body for at least one public modifier on its members. Iff a public modifier was found on any member, the migrator can add an explicit public modifier to the extension itself.

Alternatives considered

No other alternative were considered for this proposal.
Rationale

On [Date], the core team decided to (TBD) this proposal. When the core team makes a decision regarding this proposal, their rationale for the decision will be written here.

···

--
Adrian Zubarev
Sent with Airmail

Hi, Adrian. Can you explain why you want to make this change? “public” on an extension doesn’t mean anything by itself because you can’t refer to an extension as an entity in and of itself. Access modifiers are disallowed on extensions with protocols because the conformance isn’t controlled by the access modifier and we didn’t want to give the impression that it would.

There’s really no such thing as an “implicitly public extension”. An extension is just a bag of additional members and conformances. An access modifier on the extension sets the default access level of members in the extension as a convenience.

Jordan

We’re running out of time, and there is still no feedback although Chris Lattner said to consider this topic in his thread: Partial list of open Swift 3 design topics

I’m not sure if we’re allowed to PR even if there is no feedback at all.
Again this is a breaking change!

···

--
Adrian Zubarev
Sent with Airmail

Am 25. Juni 2016 um 10:45:04, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Originally I started this topic in a discussion thread but moved it now into its own proposal thread due the lack of feedback.

This proposal contains a breaking change!

You can read a formatted version here: https://gist.github.com/DevAndArtist/8f1113b6d5d0379ebf82bd227cf4a88d

Feel free to also provide feedback if you spot any typos or language mistakes I might have made.

Revising access modifiers on extensions

Proposal: SE-NNNN
Author: Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

One great goal for Swift 3 is to sort out any source breaking language changes. This proposal aims to fix access modifier inconsistency on extensions compared to other scope declarations types.

Swift-evolution thread: [Proposal] Revising access modifiers on extensions

Motivation

When declaring members on extensions which don’t have an explicit access modifier in Swift 2.2, it is possible to create an implicitly public extension by applying a public modifier to at least one extension member.

public struct A { … }

/* implicitly public */ extension A {
      
    public var member1: SomeType { … }
    /* implicitly internal */ func member2() { … }
}

/* implicitly internal */ extension A {
      
    /* implicitly internal */ var member3: SomeType { … }
}
Furthermore in Swift 2.2 it is not allowed to apply an access modifier on extensions when a type inheritance clause is present:

public protocol B { … }

// 'public' modifier cannot be used with
// extensions that declare protocol conformances
public extension A : B { … }
Proposed solution

Allow access modifier on extension when a type inheritance clause is present.

Remove the behavior of an implicit public extension.

This changes should make access modifier on extensions consistent to classes, structs and enums (and SE–0025).

The current grammar will not change:

extension-declaration → access-level-modifieropt extension type-identifier type-inheritance-clauseopt extension-body

extension-declaration → access-level-modifieropt extension type-identifier requirement-clause extension-body

extension-body → { declarationsopt }

Iff the access-level-modifier is not present, the access modifier on extensions should always be implicitly internal.

Public Api:

Current version:

//===— Implementation version —===//

public protocol Y {
      
    func member()
}

public struct X { … }

/* implicitly public */ extension X : Y {
      
    public func member() { … }
      
    /* implicitly internal */ func anotherMember() { … }
}

//===— Imported modele version —===//

public protocol Y {
      
    func member()
}

public struct X { … }

public extension X : Y {
      
    public func member() { … }
}
New Version:

//===— Implementation version —===//

public extension X : Y {
      
    public func member() { … }
      
    /* implicitly internal */ func anotherMember() { … }
}

//===— Imported modele version —===//

public extension X : Y {
      
    public func member() { … }
}
Impact on existing code

This is a source-breaking change that can be automated by a migrator, by simply scanning the extension-body for at least one public modifier on its members. Iff a public modifier was found on any member, the migrator can add an explicit public modifier to the extension itself.

Alternatives considered

No other alternative were considered for this proposal.
Rationale

On [Date], the core team decided to (TBD) this proposal. When the core team makes a decision regarding this proposal, their rationale for the decision will be written here.

--
Adrian Zubarev
Sent with Airmail

Proposal is moved to a git repo: swift-evolution/nnnn-extensions-access-modifiers.md at extensions_access_modifiers · DevAndArtist/swift-evolution · GitHub

I also updated a few things for readability.
Revising access modifiers on extensions

Proposal: SE-NNNN
Author: Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

One great goal for Swift 3 is to sort out any source breaking language changes. This proposal aims to fix access modifier inconsistency on extensions compared to other scope declarations types.

Swift-evolution thread: [Proposal] Revising access modifiers on extensions

Motivation

When declaring members on extensions which don’t have an explicit access modifier in Swift 2.2, it is possible to create an implicitly public extension by applying a public modifier to at least one extension member.

public struct A { … }

// Implicitly public
extension A {
    public var member1: SomeType { … }
     
    // Implicitly internal
    func member2() { … }
}

// Implicitly internal
extension A {

    // Implicitly internal
    var member3: SomeType { … }
}
Furthermore in Swift 2.2 it is not allowed to apply an access modifier on extensions when a type inheritance clause is present:

public protocol B { … }

// 'public' modifier cannot be used with
// extensions that declare protocol conformances
public extension A : B { … }
Proposed solution

Allow access modifier on extensions when a type inheritance clause is present.

Remove the behavior of an implicit public extension.

This changes should make access modifier on extensions consistent to classes, structs and enums (and SE–0025).

The current grammar will not change:

extension-declaration → access-level-modifieropt extension type-identifier type-inheritance-clauseopt extension-body

extension-declaration → access-level-modifieropt extension type-identifier requirement-clause extension-body

extension-body → { declarationsopt }

Iff the access-level-modifier is not present, the access modifier on extensions should always be implicitly internal.

Impact on APIs:

Current version:

/// Implementation version
///========================

public protocol Y {
    func member()
}

public struct X { … }

// Implicitly public
extension X : Y {
    public func member() { ... }
     
    // Implicitly internal
    func anotherMember() { ... }
}

/// Imported modele version
///========================

public protocol Y {
    func member()
}

public struct X { ... }

// Missing `public` modifier
extension X : Y {
    public func member() { ... }
}
New Version:

/// Implementation version
///========================

public extension X : Y {
    public func member() { ... }
     
    // Implicitly internal
    func anotherMember() { ... }
}

/// Imported modele version
///========================

public extension X : Y {
    public func member() { ... }
}
Impact on existing code

This is a source-breaking change that can be automated by a migrator, by simply scanning the extension-body for at least one public modifier on its members. Iff a public modifier was found on any member, the migrator can add an explicit public modifier to the extension itself.

Alternatives considered

No other alternative were considered for this proposal.
Rationale

On [Date], the core team decided to (TBD) this proposal. When the core team makes a decision regarding this proposal, their rationale for the decision will be written here.

Hi, Adrian. Can you explain why you want to make this change? “public” on an extension doesn’t mean anything by itself because you can’t refer to an extension as an entity in and of itself. Access modifiers are disallowed on extensions with protocols because the conformance isn’t controlled by the access modifier and we didn’t want to give the impression that it would.
Now that you mention this, I did again some tests and I think I now understand why controlling conformance with access modifier would be fatal, but I think this edge-case also can be banned easily.

My first though was looking like this:

public protocol A { func foo() }

public struct B {}

// If we had the same access control like on structs/ classes
// We could suppress the visibility (imagine: `internal struct B : A`)
internal extension B : A {
     
    /* implicitly internal */ func foo(){}
     
    // Some other custom members for this extension bag
    func boo() {}
}
Next step would be to import the module and extend B with A again, because it isn’t visible for the imported module. And that would lead to a huge problem.
BUT this is a very wrong thought, because if extensions would have the same access control as other types, this edge case cannot happen at all. The conformance itself is applied on the type B and moved to its own extension bag. But because the extended type B is public the compiler must raise an error (Fix-me?) for this particular extension that it cannot be internal when its members are coming from a public protocol and must retain public (iff the extend type is public as well).

The example from above will become this:

public protocol A { func foo() }

public struct B {}

public extension B : A {
     
    public func foo(){}
     
    // Access modifier on members won't be overridden by the extension access modifier anymore
    // And they will respect the access level boundary set by the extension
    func boo() {}
}
To sum this example up: the access modifier on extensions should only have control of its bag visibility in respect to the access level of the extended type. This would be consistent to structs, enums and classes.

In Swift we cannot suppress conformance visibility to lower visibility if the extended type is of higher or equal visibility as the protocol.

public protocol A { func foo() }

public struct B : A {
     
    // foo must retain public
    public func foo() {}
}

internal protocol C : A {
     
    // foo must retain internal
    // we cannot grant foo more visibility than the type its implemented in
    /* implicitly internal */ func foo() {}
}

// same for `fileprivate` and `private`
public > internal > fileprivate >= private (Iff private is allowed at file scope.)

However we can grant visibility to members and still hide the conformance itself.

internal protocol A { func foo() }

public protocol B : A {
     
    // foo won't be visible when imported
    func foo() {}
}

public protocol C : A {
     
    // we can grant foo more visibility than it originally had
    // foo will be visible when imported, but the
    // conformance to `A` will not be visible
    public func foo() {}
}
That said we could do the same with extensions as well.

internal protocol A { func foo() }

public struct B {}

public extension B : A {
     
    // we can grant foo visibility but still hide conformance to A
    // and move everything from `A` to an extra extension bag
    public func foo(){}
     
    // Access modifier on members won't be overridden by the extension access modifier anymore
    // And they will respect the access level boundary set by the extension
    func boo() {}
}
This whole idea is to gain more consistent control of visibility and sort out the strange access control rules extensions currently have.

If I’m correct this also would allow us to nest extension (but this needs an other thread in the future) if there is any desire to do so:

internal protocol A {}

public struct B {
     
    public struct C {}
     
    /* implicitly internal */ extension C : A {}
}

// Nested extension would remove this
internal extension B.C : A {}
One other thing I’d like to mention is this from the imported stdlib:

extension ErrorProtocol {
}
How on earth is this possible?

public protocol A {}

public extension A {
     
    internal func foo()
}
The imported module would look like this, and there is no empty extension:

public protocol A {}
Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
     
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
     
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

This will also allow us to hide default implementation for the public usage but still use it internally, which is kinda cool.

There’s really no such thing as an “implicitly public extension”. An extension is just a bag of additional members and conformances. An access modifier on the extension sets the default access level of members in the extension as a convenience.
You had me at this point, I misunderstood the behavior completely. I’ll have to rewrite the proposal to the mentioned behavior from above.

Yet I have no idea how complicated this change might be to implement, but I feel like this is a reasonable change.

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 02:41:28, Jordan Rose (jordan_rose@apple.com) schrieb:

Hi, Adrian. Can you explain why you want to make this change? “public” on an extension doesn’t mean anything by itself because you can’t refer to an extension as an entity in and of itself. Access modifiers are disallowed on extensions with protocols because the conformance isn’t controlled by the access modifier and we didn’t want to give the impression that it would.

There’s really no such thing as an “implicitly public extension”. An extension is just a bag of additional members and conformances. An access modifier on the extension sets the default access level of members in the extension as a convenience.

Jordan

There was an exchange in the past on how extensions are very different from swift nominal/structural types, and Doug even shared how adding scoping to extensions is not a trivial task by a long shot. In this context, talking about unifying modifier behaviors does not make much sense to me. I am not sure i understand what motivates this change.
Regards
(From mobile)

···

On Jun 26, 2016, at 8:59 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Proposal is moved to a git repo: https://github.com/DevAndArtist/swift-evolution/blob/extensions_access_modifiers/proposals/nnnn-extensions-access-modifiers.md

I also updated a few things for readability.
Revising access modifiers on extensions

Proposal: SE-NNNN
Author: Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

One great goal for Swift 3 is to sort out any source breaking language changes. This proposal aims to fix access modifier inconsistency on extensions compared to other scope declarations types.

Swift-evolution thread: [Proposal] Revising access modifiers on extensions

Motivation

When declaring members on extensions which don’t have an explicit access modifier in Swift 2.2, it is possible to create an implicitly public extension by applying a public modifier to at least one extension member.

public struct A { … }

// Implicitly public
extension A {
    public var member1: SomeType { … }
     
    // Implicitly internal
    func member2() { … }
}

// Implicitly internal
extension A {

    // Implicitly internal
    var member3: SomeType { … }
}
Furthermore in Swift 2.2 it is not allowed to apply an access modifier on extensions when a type inheritance clause is present:

public protocol B { … }

// 'public' modifier cannot be used with
// extensions that declare protocol conformances
public extension A : B { … }
Proposed solution

Allow access modifier on extensions when a type inheritance clause is present.

Remove the behavior of an implicit public extension.

This changes should make access modifier on extensions consistent to classes, structs and enums (and SE–0025).

The current grammar will not change:

extension-declaration → access-level-modifieropt extension type-identifier type-inheritance-clauseopt extension-body

extension-declaration → access-level-modifieropt extension type-identifier requirement-clause extension-body

extension-body → { declarationsopt }

Iff the access-level-modifier is not present, the access modifier on extensions should always be implicitly internal.

Impact on APIs:

Current version:

/// Implementation version
///========================

public protocol Y {
    func member()
}

public struct X { … }

// Implicitly public
extension X : Y {
    public func member() { ... }
     
    // Implicitly internal
    func anotherMember() { ... }
}

/// Imported modele version
///========================

public protocol Y {
    func member()
}

public struct X { ... }

// Missing `public` modifier
extension X : Y {
    public func member() { ... }
}
New Version:

/// Implementation version
///========================

public extension X : Y {
    public func member() { ... }
     
    // Implicitly internal
    func anotherMember() { ... }
}

/// Imported modele version
///========================

public extension X : Y {
    public func member() { ... }
}
Impact on existing code

This is a source-breaking change that can be automated by a migrator, by simply scanning the extension-body for at least one public modifier on its members. Iff a public modifier was found on any member, the migrator can add an explicit public modifier to the extension itself.

Alternatives considered

No other alternative were considered for this proposal.
Rationale

On [Date], the core team decided to (TBD) this proposal. When the core team makes a decision regarding this proposal, their rationale for the decision will be written here.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I did a mistake in my last reply which I’m correcting here. I meant structs instead of protocols B and C:

internal protocol A { func foo() }

public struct B : A {
      
    // foo won't be visible when imported
    func foo() {}
}

public struct C : A {
      
    // we can grant foo more visibility than it originally had
    // foo will be visible when imported, but the
    // conformance to `A` will not be visible
    public func foo() {}
}

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

internal protocol A { func foo() }

public protocol B : A {
      
    // foo won't be visible when imported
    func foo() {}
}

public protocol C : A {
      
    // we can grant foo more visibility than it originally had
    // foo will be visible when imported, but the
    // conformance to `A` will not be visible
    public func foo() {}
}

And yet another correction of the default protocol implementations part of my reply.

The imported module is correct, but the default implementation is not imported (I relied on Xcode which didn’t raise an error before I started building the project where I was using the module, and I didn’t build it at the first time).

So we’d need to implement foo by ourself.

struct B : A {
     
    public func foo() {}
}
The rest of my reply should be fine. With the same access control it would be more clearer and intuitive how the extension will be imported.

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
      
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
      
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

I completely rewritten the proposal, you can read the formatted version here:

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 12:58:53, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

And yet another correction of the default protocol implementations part of my reply.

The imported module is correct, but the default implementation is not imported (I relied on Xcode which didn’t raise an error before I started building the project where I was using the module, and I didn’t build it at the first time).

So we’d need to implement foo by ourself.

struct B : A {
      
    public func foo() {}
}
The rest of my reply should be fine. With the same access control it would be more clearer and intuitive how the extension will be imported.

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
       
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
       
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

Correcting myself over and over. Here is the correct url: swift-evolution/nnnn-extensions-access-modifiers.md at extensions_access_modifiers · DevAndArtist/swift-evolution · GitHub

···

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 14:39:41, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

I completely rewritten the proposal, you can read the formatted version here:

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 12:58:53, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

And yet another correction of the default protocol implementations part of my reply.

The imported module is correct, but the default implementation is not imported (I relied on Xcode which didn’t raise an error before I started building the project where I was using the module, and I didn’t build it at the first time).

So we’d need to implement foo by ourself.

struct B : A {
       
    public func foo() {}
}
The rest of my reply should be fine. With the same access control it would be more clearer and intuitive how the extension will be imported.

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
        
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
        
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

"The access modifier of an extension sets the default modifier of its members which has no modifier applied to them."

I cannot understand the sentence, or the following:

"If there the extension has no access modifier, then the default modifier of its members which has no explicit modifier will be internal if the extended type is either public or internal, or it will be private when the extended type is private(analogous for fileprivate)."

But more importantly, I was under the impression that Doug had hinted that private (or likely more generally scoped) conformance on extension was a slipery slope with important impact on runtime performance.

Regards
(From mobile)

···

On Jun 27, 2016, at 2:39 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

I completely rewritten the proposal, you can read the formatted version here:

https://github.com/DevAndArtist/swift-evolution/blob/19f2583209a5763880e6f6fa6738ea0c6011f2d6/proposals/nnnn-extensions-access-modifiers.md

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 12:58:53, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

And yet another correction of the default protocol implementations part of my reply.

The imported module is correct, but the default implementation is not imported (I relied on Xcode which didn’t raise an error before I started building the project where I was using the module, and I didn’t build it at the first time).

So we’d need to implement foo by ourself.

struct B : A {
      
    public func foo() {}
}
The rest of my reply should be fine. With the same access control it would be more clearer and intuitive how the extension will be imported.

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
       
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
       
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I still have no idea why you want to make this change, other than "putting an access modifier on an extension is different from putting an access modifier on a type". Are you trying to hide protocol conformances that would be otherwise public? That has much greater consequences and a much stronger domino effect than what you've discussed here.

Jordan

···

On Jun 27, 2016, at 5:44, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Correcting myself over and over. Here is the correct url: https://github.com/DevAndArtist/swift-evolution/blob/extensions_access_modifiers/proposals/nnnn-extensions-access-modifiers.md

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 14:39:41, Adrian Zubarev (adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>) schrieb:

I completely rewritten the proposal, you can read the formatted version here:

https://github.com/DevAndArtist/swift-evolution/blob/19f2583209a5763880e6f6fa6738ea0c6011f2d6/proposals/nnnn-extensions-access-modifiers.md

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 12:58:53, Adrian Zubarev (adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>) schrieb:

And yet another correction of the default protocol implementations part of my reply.

The imported module is correct, but the default implementation is not imported (I relied on Xcode which didn’t raise an error before I started building the project where I was using the module, and I didn’t build it at the first time).

So we’d need to implement foo by ourself.

struct B : A {
       
    public func foo() {}
}
The rest of my reply should be fine. With the same access control it would be more clearer and intuitive how the extension will be imported.

--
Adrian Zubarev
Sent with Airmail

Am 27. Juni 2016 um 10:38:12, Adrian Zubarev (adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>) schrieb:

Lets examine the impact on default protocol implementations:

Currently we have this behavior:

public protocol A {
    func foo()
}

extension A {
    func foo() { /* implement */ }
}
The imported version would look like this.

public protocol A {
    public func foo()
}
As the module user you have no clue that there might be a default implementation, but you sill will be able to use it, because when conforming to A you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation

struct B : A {} // This will be enough

A().foo() // this is fine
One could signal the module user that there is a default implementation by making the extension explicit public as well.

// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
        
    /// will do something cool
    func foo() { /* implement */ }
}
The result of the imported module would change and look like this:

public protocol A {
    public func foo()
}

extension A {
        
    /// will do something cool
    public func foo()
}
With the proposed change all default implementations will become visible by default and I think this is great step as well.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I still have no idea why you want to make this change, other than “putting an access modifier on an extension is different from putting an access modifier on a type”. Are you trying to hide protocol conformances that would be otherwise public?
In my proposal I cannot hide a public protocol conformance. Its not possible with normal types and it shouldn’t be possible with extensions as well. (Correct me if I’m wrong.) I really want the same access control behavior with extensions like whit classes, enums and structs. For new Swift developers such consistency would be great, because they won’t need to learn another access control behavior (second one goes to protocols).

As I showed in my proposal right now its possible to create three different version of public protocol default implementation:

public protocol A {
    func foo()
}

extension A {
    public func foo() {}
}

public extension A {
    func foo() {}
}

public extension A {
    public func foo() {}
}
If it was a type and I wanted foo to be visible I’d do this:

public struct B {
    public func foo() {}
}
With the proposed content access control we’d have only one way for public protocol default implementation:

public extension A {
    public func foo() {}
}
Yet it fells strange when there is protocol conformance that we’re not allowed to use access modifier anymore for our extension bag.

// Assume `A` has no default implementation
// Extension must retain `public` because B is `public` and `A` as well
// Explicitly public which is crystal clear
public extension B : A {
     
    // foo must retain `public`
    public func foo()
     
    // custom member - implicitly internal - like on classes enums structs
    func member() {}
}
Some rules from the proposal:

Access modifier on extensions should respect the modifier of the extended type and the protocol to which it should conform.

Public protocol:

public type + public protocol = public extension
internal type + public protocol = internal extension
private type + public protocol = private extension
Internal protocol:

public type + internal protocol = public extension or internal extension
internal type + internal protocol = internal extension
private type + internal protocol = private extension
Private protocol:

public type + private protocol = public extension or internal extension or private extension
internal type + private protocol = internal extension or private extension
private type + private protocol = private extension
Multiple protocol conformance is decided analogously by using the highest access modifier from all protocols + the access level of the extended type.

That has much greater consequences and a much stronger domino effect than what you’ve discussed here.
Could you explain this a little more? Maybe I’m to blind to see what else could break this my proposal.

···

--
Adrian Zubarev
Sent with Airmail

“The access modifier of an extension sets the default modifier of its members which has no modifier applied to them.”
public extension SomeType {
    func extensionMember() {}
}
“If there the extension has no access modifier, then the default modifier of its members which has no explicit modifier will be internal if the extended type is either public or internal, or it will be private when the extended type is private(analogous for fileprivate).”
// First
public/internal struct A {}

extension A {
     
    /* internal */ func member() {}
}

// Second
private struct B {}

extension B {
     
    /* private */ func member() {}
}
My English isn’t great, please don’t blame me for that. Feel free to correct me. I’d appreciate that. :)

But more importantly, I was under the impression that Doug had hinted that private (or likely more generally scoped) conformance on extension was a slipery slope with important impact on runtime performance.
I’m not an expert in this area, I really cannot tell. And I don’t want to dig in all these thousands of emails to find his talk. FWIW not every proposal does know the impact which will happen behind the scene. Even some simple change might have a huge impact (I don’t say this proposal is simple).

My personal vision of the access control is clarity and consistency. It would be much easier to have the same access control behavior on extensions like on classes, enums and structs.

···

--
Adrian Zubarev
Sent with Airmail

If you want consistent behavior, the proposal should just be "remove access modifiers from extensions". That way, access for members follows the same defaults as in the original type. The only purpose for access modifiers on extensions is to change the default, so if you don't like the behavior of changing the default, then the feature should be removed.

Jordan

···

On Jun 27, 2016, at 13:00, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

I still have no idea why you want to make this change, other than “putting an access modifier on an extension is different from putting an access modifier on a type”. Are you trying to hide protocol conformances that would be otherwise public?
In my proposal I cannot hide a public protocol conformance. Its not possible with normal types and it shouldn’t be possible with extensions as well. (Correct me if I’m wrong.) I really want the same access control behavior with extensions like whit classes, enums and structs. For new Swift developers such consistency would be great, because they won’t need to learn another access control behavior (second one goes to protocols).

I would be against removing access modifiers on extensions. I actually don't see anything wrong with the way extensions work with modifiers right now. Consistency for consistency's sake is never a good measurement if it is not addressing a pain point.

When ever I extend a swift standard type, I always make it "fileprivate" because I don't want to pollute autocomplete for that type.

fileprivate extension Int {
   func double() -> Int {
       return self*2
   }
}

My understanding is that private / fileprivate was renamed/introduced to help with extensions. I don't think extensions need anymore complexities. I would hardly ever use private in swift 3 for example (when it gets implemented.)

···

On Jun 27, 2016, at 3:06 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 27, 2016, at 13:00, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

I still have no idea why you want to make this change, other than “putting an access modifier on an extension is different from putting an access modifier on a type”. Are you trying to hide protocol conformances that would be otherwise public?
In my proposal I cannot hide a public protocol conformance. Its not possible with normal types and it shouldn’t be possible with extensions as well. (Correct me if I’m wrong.) I really want the same access control behavior with extensions like whit classes, enums and structs. For new Swift developers such consistency would be great, because they won’t need to learn another access control behavior (second one goes to protocols).

If you want consistent behavior, the proposal should just be "remove access modifiers from extensions". That way, access for members follows the same defaults as in the original type. The only purpose for access modifiers on extensions is to change the default, so if you don't like the behavior of changing the default, then the feature should be removed.

Jordan

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

If you want consistent behavior, the proposal should just be “remove access modifiers from extensions”. That way, access for members follows the same defaults as in the original type. The only purpose for access modifiers on extensions is to change the default, so if you don’t like the behavior of changing the default, then the feature should be removed.
I’m a little confused right now, because to me “remove access modifiers from extensions” sounds like there will be no access modifier at all.

And yes I indeed don’t like that behavior, because it has a lot potential of confusion. Don’t get me wrong, now I do understand how it works, but the way it behaves is strange.

If we’d remove this feature, would extensions automatically work like I described in the proposal?

internal protocol A {
    func foo()
}

public struct B {}

// A is hidden
public extension B : A {
     
    // `foo` can be made visible
    public func foo() {}
}

// extension will be imported as

public extension B {
    public func foo()
}
Anything else I made need to change to drive my proposal into that direction?

I really appreciate your feedback. :)

···

--
Adrian Zubarev
Sent with Airmail

Regards
(From mobile)

“The access modifier of an extension sets the default modifier of its members which has no modifier applied to them.”

I get it now.. "which do not have their own localy defined modifier"

public extension SomeType {
    func extensionMember() {}
}
“If there the extension has no access modifier, then the default modifier of its members which has no explicit modifier will be internal if the extended type is either public or internal, or it will be private when the extended type is private(analogous for fileprivate).”
// First
public/internal struct A {}

extension A {
     
    /* internal */ func member() {}
}

// Second
private struct B {}

extension B {
     
    /* private */ func member() {}
}
My English isn’t great, please don’t blame me for that. Feel free to correct me. I’d appreciate that. :)

I didn't, i just asked for clarification (i truly did not get the meaning).

But more importantly, I was under the impression that Doug had hinted that private (or likely more generally scoped) conformance on extension was a slipery slope with important impact on runtime performance.
I’m not an expert in this area, I really cannot tell. And I don’t want to dig in all these thousands of emails to find his talk. FWIW not every proposal does know the impact which will happen behind the scene. Even some simple change might have a huge impact (I don’t say this proposal is simple).

I am no expert myself. I just happened to remember what looked like a partial answer. The generic manifesto has a section on 'Private Conformances' that gives some interesting clues about what's involved in scoping extension based conformances.

···

On Jun 27, 2016, at 11:59 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

My personal vision of the access control is clarity and consistency. It would be much easier to have the same access control behavior on extensions like on classes, enums and structs.

--
Adrian Zubarev
Sent with Airmail
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

How do private or fileprivate help extensions in general? Is really laziness a strong argument over consistency and clarity?

As for your example I’d have a pretty good solution (I can’t say this will be accepted, but the idea is great, at least I think that way. I’d propose for that feature after Swift 3 drops):

// Use correct consistent access control
fileprivate extension Int {
     
    // every member of a nameless group would be `fileprivate`
    fileprivate group {
     
       func double() -> Int {
           return self*2
       }
        
       func member1() {}
       func member2() {}
       func member3() {}
       func member4() {}
    }
}
A group could do way more than just in this example: https://gist.github.com/DevAndArtist/c74f706febf93452999881335f6ca1f9

We’d move the behavior out into its own more powerful feature.

···

--
Adrian Zubarev
Sent with Airmail

Am 28. Juni 2016 um 03:14:08, Jose Cheyo Jimenez (cheyo@masters3d.com) schrieb:

I would be against removing access modifiers on extensions. I actually don't see anything wrong with the way extensions work with modifiers right now. Consistency for consistency's sake is never a good measurement if it is not addressing a pain point.

When ever I extend a swift standard type, I always make it "fileprivate" because I don't want to pollute autocomplete for that type.

fileprivate extension Int {
func double() -> Int {
return self*2
}
}

My understanding is that private / fileprivate was renamed/introduced to help with extensions. I don't think extensions need anymore complexities. I would hardly ever use private in swift 3 for example (when it gets implemented.)

I apologize for all the typos I make, I’m way too tired and it’s already late night for me. (I just feel like I need to apologize :] )

I meant:

Anything else I may need to change to drive my proposal into that direction?

···

--
Adrian Zubarev
Sent with Airmail

Am 28. Juni 2016 um 00:17:35, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

If you want consistent behavior, the proposal should just be “remove access modifiers from extensions”. That way, access for members follows the same defaults as in the original type. The only purpose for access modifiers on extensions is to change the default, so if you don’t like the behavior of changing the default, then the feature should be removed.
I’m a little confused right now, because to me “remove access modifiers from extensions” sounds like there will be no access modifier at all.

And yes I indeed don’t like that behavior, because it has a lot potential of confusion. Don’t get me wrong, now I do understand how it works, but the way it behaves is strange.

If we’d remove this feature, would extensions automatically work like I described in the proposal?

internal protocol A {
    func foo()
}

public struct B {}

// A is hidden
public extension B : A {
      
    // `foo` can be made visible
    public func foo() {}
}

// extension will be imported as

public extension B {
    public func foo()
}
Anything else I made need to change to drive my proposal into that direction?

I really appreciate your feedback. :)

--
Adrian Zubarev
Sent with Airmail