[swift-evolution] [Draft] Harmonize access modifiers for extensions

Xiaodi Wu xiaodi.wu at gmail.com
Mon Jul 18 02:50:02 CDT 2016


All righty, thanks for all of your feedback. I've worked on revising the
proposal this evening, re-reading previous documents and messages and
re-analyzing what people meant. I think Jose is absolutely right in the
end, and the proposal has turned out like he suggested. Here is the current
draft below:

Harmonize access modifiers for extensions

   - Proposal: SE-XXXX
   <https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md>
   - Author: Xiaodi Wu <https://github.com/xwu>
   - Status: Awaiting review
   - Review manager: TBD

<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#introduction>
Introduction

During discussion of SE-0119
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/0119-extensions-access-modifiers.md>,
some voiced concern that writing public extension increases the default
access level for members declared within that extension, whereas writing public
class or public struct does not do the same.

This behavior is explained as follows: since extensions have no runtime
representation and are not first-class entities, access modifiers on
extensions serve as a shorthand to set the default access level for
members. Certain members of the community have indicated that such behavior
makes extensions a natural grouping construct.

A general principle of Swift, recently strengthened by proposals such as
SE-0117
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/0117-non-public-subclassable-by-default.md>,
has been that public API commitments should require explicit opt-in. Given
the different behavior of classes and structs, the fact that extensions
allow public methods to be declared without spelling out public at the
declaration site has been called "confusing" or "odd."

The aim of this proposal is to, in as conservative a manner as possible,
require explicit use of public for public methods declared inside any
extension.

Swift-evolution threads:

   - [Proposal] Revising access modifiers on extensions
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160620/022144.html>
   - [Review] SE-0119: Remove access modifiers from extensions
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024224.html>
   - [Draft] Harmonize access modifiers for extensions
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024522.html>

<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#motivation>
Motivation

Consider the following:

public struct foo {
  func frobnicate() { } // internal
}
public extension foo { }

public struct bar { }
public extension bar {
  func frobnicate() { } // public
}

This outcome is explained by rules regarding access modifiers specifically
on extensions Swift 2
<https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html>,
which is slated for preservation in Swift 3 as detailed in SE-0025
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/0025-scoped-access-level.md>.
However, it is arguably surprising that, of two declarations spelled
identically, one leads to a public API commitment while the other does not.
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#proposed-solution>Proposed
solution

The proposed solution is to amend access modifier rules to eliminate the
possibility of defaulting the access level of members declared inside an
extension to public.
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#detailed-design>Detailed
design

Amend access modifier rules as follows:

An extension may optionally be marked with an explicit access modifier that
specifies the default scope [see SE-0025]. However, such an explicit
modifier *must not match (or exceed) the original type's access level*.

This rule would preserve the possibility of using extensions as grouping
constructs. At the same time, it would (1) remove the possibility of
writing public extension to default the access level of members to public;
and (2) clarify the notion that an access modifier on an extension is a
shorthand and not a way to create a first-class entity by disallowing
repeating of the original type's access level.

*Explicit* access modifiers will continue to set the maximum allowed access
within an extension, as clarified in SE-0025.
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#alternatives-considered>Alternatives
considered

One alternative is to eliminate explicit access modifiers on extensions
altogether. As an advantage, this would further clarify the mental model
that extensions are not their own first-class entities. As a disadvantage,
extensions cease to be an access modifier grouping construct, which some
users really like.
<https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md#acknowledgments>
Acknowledgments

Thanks to all discussants on the list, especially Adrian Zubarev, Jose
Cheyo Jimenez, and Paul Cantrell.


On Sun, Jul 17, 2016 at 11:08 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:

> I understand how it works.
>
> By aligning access modifier rules inside extensions with those inside
> types, all other modifiers would continue to work as it does now
> (implicitly internal members would be limited by the upper bound). The only
> change in this respect is removing the ability to have public API without
> writing `public func`.
>
> On Sun, Jul 17, 2016 at 11:01 Adrian Zubarev via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> I tackled it as an upper bound but highly rejected by the community.
>> That’s exactly what my proposal was all about. An upper boundary would be
>> more elegant, but I still see arguments ‘because it’s not a type’.
>>
>> I could live without access modifiers on extensions in general.
>>
>> The default access modifier rule permits public methods to be written
>> without public func
>>
>> You meant this?
>>
>> public extension SomeType {
>>     // I don't need to write public
>>     func foo() {}
>>     var computed: Type {}
>> }
>>
>> This applies to all access modifiers which are not optional (like
>> internal):
>>
>> public SomeType
>> fileprivate extension SomeType {
>>     // I don't need to repeat fileprivate
>>     func foo() {}
>>     var computed: Type {}
>> }
>>
>> // which is more likely `fileprivate` because it's on file scope
>> private extension SomeType {
>>     // even if the inner access modifier would pretend to be private
>>     // since the extension is on filescope, everything will be `fileprivate`
>>     func foo() {}
>>     var computed: Type {}
>> }
>>
>>
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> Am 17. Juli 2016 um 17:50:31, Xiaodi Wu (xiaodi.wu at gmail.com) schrieb:
>>
>> The proposal is that the access modifier for an extension will either be
>> removed entirely or remain as an upper bound, losing its function as a
>> default access modifier. The default access modifier rule permits public
>> methods to be written without `public func`; this is a proposal to remove
>> that feature because it is a source of confusion.
>> On Sun, Jul 17, 2016 at 10:43 Adrian Zubarev via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> I still don’t catch to point here. There is no implicit public there.
>>> It’s explicit set by the default access modifier of extensions. It’s how
>>> they work and how they should remain (at least as long the community want default
>>> access modifier to exist on extensions). Disallowing setting public on
>>> extensions when you extend a public type makes no sense. If you want your
>>> member to be internal like it’s in types, then remove the access
>>> modifier from extension and all member will follow the type access modifier.
>>>
>>>
>>> --
>>> Adrian Zubarev
>>> Sent with Airmail
>>>
>>> Am 17. Juli 2016 um 17:37:02, Xiaodi Wu (xiaodi.wu at gmail.com) schrieb:
>>>
>>> That's a good point. I will incorporate these into a revised draft. Only
>>> two things will change:
>>>
>>> ```
>>> public struct Foo {
>>>   // implicitly internal
>>>   func frobnicate1() { }
>>> }
>>> public extension Foo {
>>>   // currently implicitly public
>>>   //
>>>   // depending on which alternative is adopted,
>>>   // the proposal will either prohibit `public extension`
>>>   // or this method will be implicitly internal
>>>   func frobnicate2() { }
>>> }
>>> ```
>>>
>>> ```
>>> internal struct Bar {
>>>   // permitted by SE-0025 without a warning
>>>   // this method can only be accessed within module anyway
>>>   // because `internal struct` bounds access of its members
>>>   public func frobnicate1() { }
>>> }
>>> extension Bar {
>>>   // not permitted by SE-0025
>>>   //
>>>   // after proposal, this will also be permitted without a warning
>>>   // and this method will also be accessible only within module
>>>   public func frobnicate2() { }
>>> }
>>> ```
>>>
>>> On Sun, Jul 17, 2016 at 1:50 AM, Adrian Zubarev via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>>> I’m struggling to understand your proposal, can you provide some
>>>> specific code samples how it works now and what will change. The example
>>>> from the draft doesn’t help my understanding. :/
>>>>
>>>>
>>>> --
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>>
>>>> Am 17. Juli 2016 um 04:40:45, Xiaodi Wu via swift-evolution (
>>>> swift-evolution at swift.org) schrieb:
>>>>
>>>> On Sat, Jul 16, 2016 at 7:56 PM, Jose Cheyo Jimenez <
>>>> cheyo at masters3d.com> wrote:
>>>>
>>>>> I think you can simplify this proposal by just saying something like
>>>>> this and give a couple of examples that are easy to follow:
>>>>>
>>>>> Disallow explicit public access modifier on non-protocol-conforming
>>>>> type extensions.
>>>>>
>>>>
>>>> It took me a while to process what you're trying to say here, but this
>>>> is a good idea and would go along well with the first draft's proposed
>>>> solution. I will spell it out. (If we say that you can use an explicit
>>>> modifier only to lower the access level of members, then `public` as an
>>>> explicit modifier could be entirely disallowed.)
>>>>
>>>>
>>>>>
>>>>> I think if you only focus on that breaking change then the proposal
>>>>> will have a good chance of getting accepted and fixing the immediate issue
>>>>> of public. There is a reason why protocol conforming extensions do not
>>>>> allow explicitly saying public
>>>>> `public extension Type: Protocol {}` // public not allowed
>>>>>
>>>>
>>>> Actually, no modifiers are allowed in that scenario, IIUC.
>>>>
>>>>>
>>>>> In essence we will be asking for the same behavior for types.
>>>>>
>>>>> Allowing methods declared inside extensions to have a higher declared
>>>>> visibility is not a breaking change and can be introduced later.
>>>>>
>>>>
>>>> It is a breaking change in that I am proposing that the rules be
>>>> harmonized so that the implicit default access level will be notionally
>>>> `internal` (there are follow-on benefits to this change). That cannot be
>>>> changed later.
>>>>
>>>>
>>>>> Nobody wants private extensions or implicit internal extensions to go
>>>>> away. :)
>>>>>
>>>>
>>>> I know that there are people who don't want it to go away. That was why
>>>> the first draft proposed keeping them, but it sounds like it would make for
>>>> an illogical system. I know that Jordan and John have both indicated that
>>>> they don't think it's worth keeping around but don't seem to feel too
>>>> strongly about it, and I think I feel the same way (leaning towards not
>>>> keeping them, but don't feel very strongly). I will definitely feature this
>>>> concern (using extensions as access modifier groups) prominently in the
>>>> proposal and hope for a robust discussion to see how it plays out with the
>>>> community and core team.
>>>>
>>>>
>>>>
>>>>> On Jul 16, 2016, at 4:22 PM, Xiaodi Wu via swift-evolution <
>>>>> swift-evolution at swift.org> wrote:
>>>>>
>>>>> On Sat, Jul 16, 2016 at 6:10 PM, David Hart <david at hartbit.com> wrote:
>>>>>
>>>>>> This proposal really confuses me. Two comments:
>>>>>>
>>>>>> 1) With the proposal, we loose the ability to use access modifiers on
>>>>>> extensions as a way of grouping members by access. That's a huge loss for
>>>>>> me.
>>>>>>
>>>>>
>>>>> You lose the ability to group public members only. That part is
>>>>> intentional, so that only methods declared with `public func` are public.
>>>>>
>>>>>
>>>>>> 2) If we adopt the proposal, I now have no idea what explicit access
>>>>>> modifiers on extensions do.
>>>>>>
>>>>>
>>>>> I propose keeping explicit access modifiers because previous comments
>>>>> on this list have said that it's useful for grouping members by access. You
>>>>> can continue to use extensions to group fileprivate members of an internal
>>>>> type, or internal members of a public type.
>>>>>
>>>>>
>>>>>
>>>>>> More generally, I don't understand this proposal as it's trying to
>>>>>> apply the same access modifier rules on extensions as for types but
>>>>>> extensions are not types. They are just a declaration for extending types
>>>>>> which already have an access level.
>>>>>>
>>>>>> On 16 Jul 2016, at 20:04, Xiaodi Wu via swift-evolution <
>>>>>> swift-evolution at swift.org> wrote:
>>>>>>
>>>>>> With the impending withdrawal of SE-0119 and the closing window for
>>>>>> (most) source-breaking changes, I thought I'd draft up a proposal to
>>>>>> address some of the key points raised in that discussion.
>>>>>>
>>>>>> The proposed changes are deliberately limited in scope to
>>>>>> rationalizing access modifier rules without adding any new facilities (such
>>>>>> as conformances of lower visibility than the type), which might be more
>>>>>> appropriate for the Swift 4 timeline.
>>>>>>
>>>>>> I hope this will prove satisfactory to the community :)
>>>>>>
>>>>>>
>>>>>> Harmonize access modifiers for extensions
>>>>>>
>>>>>>    - Proposal: SE-XXXX
>>>>>>    <https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/XXXX-harmonize-access-modifiers.md>
>>>>>>    - Author: Xiaodi Wu <https://github.com/xwu>
>>>>>>    - Status: Awaiting review
>>>>>>    - Review manager: TBD
>>>>>>
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#introduction>
>>>>>> Introduction
>>>>>>
>>>>>> During discussion of SE-0119
>>>>>> <https://github.com/xwu/swift-evolution/blob/harmonize-access-modifiers/proposals/0119-extensions-access-modifiers>,
>>>>>> the community articulated the view that access modifiers for extensions
>>>>>> were and should continue to be subject to the same rules as access
>>>>>> modifiers for types. Unfortunately, it is not factually true today; this
>>>>>> proposal aims to make it so.
>>>>>>
>>>>>> Swift-evolution threads:
>>>>>>
>>>>>>    - [Proposal] Revising access modifiers on extensions
>>>>>>    <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160620/022144.html>
>>>>>>    - [More to be added here]
>>>>>>
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#motivation>
>>>>>> Motivation
>>>>>>
>>>>>> Consider the following:
>>>>>>
>>>>>> public struct foo {
>>>>>>   func frobnicate() { } // implicitly internal
>>>>>> }
>>>>>> public extension foo { }
>>>>>>
>>>>>> public struct bar { }
>>>>>> public extension bar {
>>>>>>   func frobnicate() { } // implicitly public, according to SE-0025
>>>>>> }
>>>>>>
>>>>>> According to SE-0025, a method moved from the body of a public struct
>>>>>> into a public extension becomes public without modification. This is
>>>>>> surprising behavior contrary to Swift's general rule of not exposing public
>>>>>> API by default.
>>>>>>
>>>>>> Furthermore, SE-0025 now permits the owner of a type to design access
>>>>>> for members as though the type will have a higher access level than it
>>>>>> currently does. For example, users will be able to design public methods
>>>>>> inside an internaltype before "flipping the switch" and making that
>>>>>> type public. The same approach is prohibited by SE-0025 for
>>>>>> extensions, although conceptually it need not be.
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#proposed-solution>Proposed
>>>>>> solution
>>>>>>
>>>>>> The proposed solution is to change access modifier rules for
>>>>>> extensions with the following effect: if any method (or computed property)
>>>>>> declared within the body of a type at file scope is moved without
>>>>>> modification into the body of an extension in the same file, the move will
>>>>>> not change its accessibility.
>>>>>>
>>>>>> In code:
>>>>>>
>>>>>> struct foo {
>>>>>>   // Any method declared here...
>>>>>> }
>>>>>> extension foo {
>>>>>>   // ...should have the same visibility when moved here.
>>>>>> }
>>>>>>
>>>>>> This implies that public API commitments will need to be annotated as
>>>>>> public at declaration sites inside an extension just as it must be
>>>>>> at declaration sites inside types.
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#detailed-design>Detailed
>>>>>> design
>>>>>>
>>>>>>    1. Declarations inside the extension will, like declarations
>>>>>>    inside types, have a default access level of internal.
>>>>>>    2. The compiler should not warn when a broader level of access
>>>>>>    control is used for a method (or computed property, etc.) declared within
>>>>>>    an extension with more restrictive access. This allows the owner of the
>>>>>>    extension to design the access level they would use for a method if the
>>>>>>    type or extension were to be made more widely accessible.
>>>>>>    3. An extension declared without an explicit access modifier will
>>>>>>    have the same access level as the type being extended.
>>>>>>    4. An extension declared without protocol conformance may
>>>>>>    optionally use an explicit access modifier to provide an upper bound for
>>>>>>    the visibility of its members.
>>>>>>
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#alternatives-considered>Alternatives
>>>>>> considered
>>>>>>
>>>>>>    - One alternative, still open for consideration, is to eliminate
>>>>>>    #4 and disallow explicit access modifiers on extensions. As an advantage,
>>>>>>    this would clarify the mental model that extensions are not their own
>>>>>>    entities, as they cannot be referred to by name and have no runtime
>>>>>>    representation. As a disadvantage, extensions cease to be an access
>>>>>>    modifier grouping construct, which some users really like.
>>>>>>
>>>>>>
>>>>>> <https://github.com/xwu/swift-evolution/tree/harmonize-access-modifiers#acknowledgments>
>>>>>> Acknowledgments
>>>>>>
>>>>>> Thanks to all discussants on the list, especially Adrian Zubarev and
>>>>>> Jose Cheyo Jimenez.
>>>>>>
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution at swift.org
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>
>>>>>>
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>
>>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>> _______________________________________________
>> 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/20160718/2e46ae87/attachment.html>


More information about the swift-evolution mailing list