[Review] SE-0169: Improve Interaction Between private Declarations and Extensions

Sent from my iPhone

What is your evaluation of the proposal?

Strong -1. Just rename ‘fileprivate’ to be less annoying.

So, we keep being told it won't happen and our current Bly suggestion discussing this proposal cannot be to just keep asking for it, can it?

We were told we can’t do the double rename. I believe renaming ‘fileprivate’ is still on the table (or at least it will have to be at some point when we consider submodules).

We could, in some future update, introduce a new keyword and deprecate 'fileprivate'. Whether this impacts submodules really remains to be seen — there are about twenty different things that people mean by "submodules", and it's not at all clear that we should be aiming for a sub-file granularity instead of a super-file granularity. I understand that some people just seem personally offended by the existence of files, though.

We will not be changing the meaning of 'private' after Swift 4.

John.

···

On Apr 7, 2017, at 8:32 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 7, 2017, at 3:51 AM, Goffredo Marocchi <panajev@gmail.com <mailto:panajev@gmail.com>> wrote:
On 7 Apr 2017, at 09:56, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If renaming ‘fileprivate’ is truly off the table, we should definitely NOT accept this proposal (for all the reasons I and others have stated). We should keep the status quo, horrible as it may be.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I think that is why he is saying (and I agree), that ‘fileprivate’ needs to be the soft-default. That way private will mean something.

Keywords aren't completely arbitrary. Programmers will always reach for 'private' first, both from familiarity with other languages and because of the obvious contrast with 'public'. You can teach them not to do that, but that then becomes something they have to specifically learn instead of something that comes intuitively.

His point that this gets rid of the primary use-case of ‘private’ (over ‘fileprivate’) is also extremely relevant.

That depends very much on what you put in a single file. If you generally just put a single type and its extensions in a file, then yes, 'private' and 'fileprivate' are essentially the same, other than for nested types. If you put other code in the same file, 'private' remains meaningfully stronger than 'fileprivate'. In both cases, 'private' is reinforced as a default choice; but you do lose that ability to prevent extensions from directly accessing the stored properties of a type, which I agree is a significant loss.

John.

···

On Apr 7, 2017, at 8:37 AM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 7, 2017, at 4:51 AM, Gwendal Roué via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Le 7 avr. 2017 à 13:44, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

No. I believe it makes the language worse, not better. It doesn’t address the real problems with access control. The largest problem is the inability to form scopes between files and the entire module. The problem with `fileprivate` and `private` is a naming problem, not a semantics problem.

This is the base of your argument, and I think it is wrong, considering that code is a living matter, not a static one.

Too many properties initially declared as `private` have to be declared `fileprivate` later, because the code is evolving. And this change is usually performed just to tame a compiler error.

This is why the current private/fileprivate situation is actually a semantics problem. Private is not stable enough to mean anything.

Gwendal Roué

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

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

I suggested that a while ago. Although I still think it’s the best solution (in addition to the benefits you mentioned, partials could also contain stored properties without making the behavior of extensions inconsistent), it didn’t seem to go over very well on the list, with people balking at the prospect of introducing another keyword.

Charles

···

On Apr 8, 2017, at 7:19 AM, Neil via swift-evolution <swift-evolution@swift.org> wrote:

I agreed with Charlie, but I think there’s another option.

The access control problems that both SE-0159 and SE-0169 are attempting to address can be resolved not by changing the definition of the existing access modifiers, but refocussing the use of extensions.

One such way would be to introduce a `partial` modifier. It would be similar to C#’s partial modifier. The proposed partial modifier would be purely additive and it would go a long way to mitigate the access control issues inherent in extension-oriented design.

The key characteristics of partial-oriented design would be:

- Partials would allow the splitting of implementations into multiple logical units without the same-file restriction.
- Partial definitions of a type are restricted to same module. If you wish to add functionality to a type external to its defining module, use an extension.
- Partials would provide greater clarity between additive extension and the original implementation.
- Within a partial, private would be type-private.
- Within an extension, private would be scoped-private (the status quo).

I do see that the last two points may introduce some friction. Particularly because:

- Within a partial, fileprivate would be more restrictive than private.
- Within an extension, fileprivate would be less restrictive than private (the status quo).

However, I don’t see these as too challenging for educators or developers as they are differentiated by their top-level scope.

On 07/04/2017, 05:57, "Charlie Monroe via swift-evolution" <swift-evolution-bounces@swift.org on behalf of swift-evolution@swift.org> wrote:

Simply as long as it's file-based, it's not a solution, it's just another attempt to satisfy some part of the community. I'm not saying that the current access levels are perfect, but I still believe the way to go is to either use submodules, and/or introducing the concept of "protected" members.

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

I agreed with Charlie, but I think there’s another option.

The access control problems that both SE-0159 and SE-0169 are attempting to address can be resolved not by changing the definition of the existing access modifiers, but refocussing the use of extensions.

One such way would be to introduce a `partial` modifier. It would be similar to C#’s partial modifier. The proposed partial modifier would be purely additive and it would go a long way to mitigate the access control issues inherent in extension-oriented design.

The key characteristics of partial-oriented design would be:

- Partials would allow the splitting of implementations into multiple logical units without the same-file restriction.

I really like the idea of partial but it would need to be restricted to the same file. This would allow partial to just be a compile time feature which just zips up a type together. Another great feature would be that it could allow stored properties because it is only a compile time feature. Partial could be used to communicate to the user that this type has additional members and properties declared somewhere else in the file.

+1 for file scope partial.

···

On Apr 8, 2017, at 5:19 AM, Neil via swift-evolution <swift-evolution@swift.org> wrote:

- Partial definitions of a type are restricted to same module. If you wish to add functionality to a type external to its defining module, use an extension.
- Partials would provide greater clarity between additive extension and the original implementation.
- Within a partial, private would be type-private.
- Within an extension, private would be scoped-private (the status quo).

I do see that the last two points may introduce some friction. Particularly because:

- Within a partial, fileprivate would be more restrictive than private.
- Within an extension, fileprivate would be less restrictive than private (the status quo).

However, I don’t see these as too challenging for educators or developers as they are differentiated by their top-level scope.

On 07/04/2017, 05:57, "Charlie Monroe via swift-evolution" <swift-evolution-bounces@swift.org on behalf of swift-evolution@swift.org> wrote:

Simply as long as it's file-based, it's not a solution, it's just another attempt to satisfy some part of the community. I'm not saying that the current access levels are perfect, but I still believe the way to go is to either use submodules, and/or introducing the concept of "protected" members.

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

>
> > What is your evaluation of the proposal?
>
> If this is the last option we have to change the status quo, any renaming is off the table, no further changes after Swift 4, and it's either this or being stuck with 'fileprivate' until the end of time, then +1 from me. It will increase the convenience of access control for people like me who see types and their extensions as parts of the same entity, just spread visually across neighboring blocks. In almost any other language these two would indeed be one entity, since most languages don't have any way of dividing types into pieces this way.
>
> However, IMHO any of these would be a better solution:

I'd like to respond briefly to this to clarify the Core Team's decisions about what solutions are under consideration, both now and in the future. By doing this, I don't mean to pressure you towards any particular stance. The Core Team asked for this to be proposed because we wanted to know how the community felt about it; we are not specifically trying to get it approved, at least as a group.

> 1) Rename 'private' to something else ('scoped'?) and rename 'fileprivate' back to 'private'

The Core Team has rejected making such a major change in the interpretation of 'private'. 'private' will be tied to scopes, now and forever. The only question is whether extensions of the same type within a file should be considered part of the same scope for the purposes of 'private'. Swift 4 is the deadline for making that change; if it, too, is rejected, 'private' will be purely lexical forever.

> 2) Rename 'fileprivate' to something more friendly (I liked the 'local' suggestion that Vladimir made today)

The Core Team is willing to consider adding a new keyword to replace 'fileprivate', but not in Swift 4.

Speaking just for myself, I don't think we'd accept such a change purely for aesthetics;

If I recall correctly, Chris in post-review discussions communicated that a new keyword to replace `fileprivate` would be considered if `fileprivate` turned out to be commonly used enough to be aesthetically problematic?

Yes. We would consider it. Chris probably believes that we're more likely to accept that change than I do; that's why I was careful to say that I was just speaking for myself. (Chris didn't need to say that explicitly in his message because he wasn't explicitly speaking for the Core Team in other parts.)

I bring this up because, in replying to BJ Homer, it occurs to me that there is an interesting solution. If--as I understand--successive access modifiers being supersets of preceding ones is a non-negotiable part of the design, then type-based access modifiers cannot be accommodated in Swift because of the existence of extensions. Ergo, the word "protected" cannot ever be used to mean what it does in C++, etc. It is a perfectly nice word that by its dictionary meaning is plausibly intermediate between "private" and "internal," which we can repurpose to mean protected by the end of file! By co-opting this word, it has the key virtue of definitively demonstrating that Swift cannot and will not support access modifiers that are tied to types.

I wouldn't say that successive access modifiers being supersets of existing ones is a part of the design. We've been reluctant to change *existing* modifiers in ways that aren't just weakening them, but that's different.

That said, "protected" is a pretty valuable keyword, and there have been several other interesting proposals for it, especially for things that lie on the other side of "internal". I wouldn't want to burn "protected" until we've gotten a complete enough picture of what access problems library developers are having that we're sure it's not more useful there.

John.

···

On Apr 8, 2017, at 4:12 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Apr 7, 2017 at 11:34 PM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On Apr 7, 2017, at 8:12 PM, Jakub Suder via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

it would have to be because 'fileprivate' seemed inappropriate for some new generalization, e.g. if we added sub-file submodules and wanted 'fileprivate' to allow access only within the submodule. That is assuming a lot about how a future submodule feature would work, and we aren't going to design that feature specifically with a goal of replacing this keyword, and frankly we don't know when we're going to take that on at all. I would caution people against assuming that 'fileprivate' will be renamed.

> 3) Postpone this until we add submodules, but with the assumption that it will be possible to make some source-breaking changes at that point

The Core Team is not willing to change the basic design of 'private' in future releases of Swift. If some feature — perhaps submodules — demands that we define its interaction with 'private', the design of that interaction will have to feel natural and consistent with the at-that-point extant design of 'private'. For example, it would not be acceptable if, say, adding a submodule declaration to a file suddenly changed the interpretation of 'private'.

An option (4) that you didn't list but which I should cover for completeness would be to add new keywords in the future with new interpretations. This is something that the Core Team is willing to consider. However, speaking just for myself again, I find it unlikely that we would add new access control features just to express increasingly-precise refinements; it would have to be something that felt necessary because of some real inadequacy in the existing access-control levels as applied to some other new feature (e.g. submodules).

John.

> The thing I don't like about this proposal (or status quo) - apart from the fact that it will make people who like the current strict private unhappy - is that 'private' even right now means kind of two different things:
>
> - for a property or a method defined in a class/struct, it means "available only inside this type"
> - for a private global variable/constant/function, or a private type or extension, it means "available in this file" i.e. the same as 'fileprivate'
>
> So if we're worried that this proposal makes the meaning of 'private' unclear - it already is unclear. Wouldn't it be much more clear if private global variables, functions and classes were required to use the access level that means "available in this file", since that's how they actually work? (as long as this level is not named 'fileprivate' :)
>
> And the other access level, the "available only inside this type" (private / scoped), could only be used for things that are actually contained inside a type, and wouldn't have two different meanings depending on whether it's at the root of the file or nested inside something else.
>
> I really believe that even though this is kind of painful for everyone, it's worth spending time to figure out a solution that satisfies most of us and makes the language clearer and friendlier in the long term, even if it means breaking compatibility again. Do we want to be stuck with an imperfect solution 10 years from now, because we didn't want to do this last breaking change now?
>
>
> > Is the problem being addressed significant enough to warrant a change to Swift?
>
> I think almost everyone here agrees it is significant.
>
>
> > Does this proposal fit well with the feel and direction of Swift?
>
> This is a difficult question. IMHO it would definitely fit much better with the direction of Swift if we bit the bullet and did whatever we agree will make the language simpler and better long term, regardless how many changed lines in git this will cause when Swift 4 is released.
>
>
> > If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
>
> I've used many languages, but I think in none of them it was a common thing (or even a possibility) to split types into several blocks like we do in Swift with extensions, so the main issue we're talking about didn't exist there. In Ruby you can kind of do the same thing with modules - if you have a private method in a module, you can access it from methods in the main type, even if they're defined in a different file. (But Ruby isn't very strict about access control in general, e.g. it allows you to call private methods on any object via #send).
>
>
> > How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
>
> I've read or skimmed through most of the messages in the recent threads about this and the last proposal.
>
> Disclaimer: I have very little experience with Swift 3 (but plenty with Swift 2.x).
>
> Kuba
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

  • What is your evaluation of the proposal?

Positive.

I could not stress enough how the attitude behind such a proposal is *positive*.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. private / fileprivate had a tremendous design issue: developers would jump from private to fileprivate just to shut up the compiler. Now this won't happen unless an actual design decision.

  • Does this proposal fit well with the feel and direction of Swift?

Yes.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Reasonably well.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Have pity. It's very difficult to follow this topic that has generated thousands of messages. I vote +1 because the proposal looks reasonable, balanced, and focused on regular developers. <rant>I wish people who insists on splitting hairs about access control would give us a break already.</rant>

Gwendal Roué

···

Le 11 avr. 2017 à 13:00, David Hart via swift-evolution <swift-evolution@swift.org> a écrit :

On 11 Apr 2017, at 08:48, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

-1 (strong)

I think I've read all messages and haven't much to add — so just to sum up my concerns in order:
— It makes access control more complicated
For me, it's not about the complexity itself (it's not terrible huge, after all), but I believe in the beauty of simplicity, which is already quite flawed.
— It adds no power to the language
The proposal solves no real problem, but rather encourages a certain style of programming, which is quite popular among authors of styleguides, but has no clear net gain.

While your other points are defendable, I have to disagree here. It does bring value to a population of developers (me included) for whom that style of programming adds a lot of readability. It's part of style guides for a reason.

— It diminishes the need for fileprivate without making it redundant
It would be nice to get rid of an access level, but having four common ones and fileprivate as an exotic outlier doesn't feel right for me.

Doesn't the argument of progressive disclosure sway you here? open is already an outlier that only library authors will play with, and that's a good thing. It reduces the number of access level truly useful on a day to day basis, while leaving the others for more narrow purposes.

— It is a mockery for SE-0025
I never liked "new private", but it was decided that this sort of encapsulation is important enough to justify 33% increase in the number of access levels, so I don't like to see this watered down
— It is a breaking change
For me personally, that doesn't really count as an argument on its own, and I would gladly take the inconvenience of incompatibility in exchange for a tiny improvement of the language; but in combination with the other points, it increases opposition.

I spend some time thinking about nested extensions (which would achieve the goal of this proposal naturally) and think that SE-0169 would lead to some questionable possibilities of short-circuiting access control:

class Outer {
  private class Inner {
    private var x = 0
  }
}

/*
.
.
.
*/

extension Outer.Inner {
  public func f() {
    print(x)
  }
}

As I read the proposal, there is no doubt that this would be allowed — but without that detailed information, I'm not sure if anybody would expect that two levels of private still is not enough to keep something secret…

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

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

I have used C++ `friend`, like 15 years ago, and I can't say I lack it today!

Still, under this proposal, Swift's `fileprivate` will cover some use cases of C++ `friend`, and also `protected`: after all, some blessed types or subclasses will have access to some otherwise unavailable members. If I'm right, we should see "Swift fileprivate considered harmful" posts sooner or later :-)

BTW - I've just realized that I've voted twice on this proposal - apologies!

Gwendal

···

Le 8 avr. 2017 à 00:45, Jordan Rose <jordan_rose@apple.com> a écrit :

On Apr 7, 2017, at 04:18, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

- the private/fileprivate qualifier used not to be a intrinsic property of an object (because one had to move from private to fileprivate as soon as an extension was added). Now private/fileprivate can be made meaningful, and above all *stable*. A scenario where private is turned into a fileprivate now involves something called "friendship" in C++: fileprivate now reflects *actual design intent*, not "shut up this stupid compiler".

I, uh, wish to object to your implication that C++ 'friend' reflects actual design intent in most cases. :-)

— It adds no power to the language
The proposal solves no real problem, but rather encourages a certain style of programming, which is quite popular among authors of styleguides, but has no clear net gain.

While your other points are defendable, I have to disagree here. It does bring value to a population of developers (me included) for whom that style of programming adds a lot of readability. It's part of style guides for a reason.

… and it worked well enough, even with only three access levels. Is it really that beneficial to have properties that are only visible to certain extensions in a given file?
If I had a type that
a) implements some protocols (or has some other motivation to be split)
b) has members that should be hidden, but exposed to a single other type (fileprivate would be just fine without this premise)
c) has no other members that should be shared with a different alien type (that would force us to expose those parts to a type that should have no access)
d) has elements that are so fragile that they should be protected from any other type (SE-0169)
e) at the same time, it has to be fine that those fragile members of a nontrivial type can be accessed from all of its extensions that are located in the same file

SE-0169 would be the perfect answer to such a scenario, but I don't think I will encounter such types often:
As soon as one requirement isn't fulfilled, the whole purpose of those complicated modifiers is defeated, because our type leaks data that should be contained securely.
The construct of fileprivate and private with different meanings offers no fundamental benefit over internal — it just delays the issues it wants to solve a tiny bit.

— It diminishes the need for fileprivate without making it redundant
It would be nice to get rid of an access level, but having four common ones and fileprivate as an exotic outlier doesn't feel right for me.

Doesn't the argument of progressive disclosure sway you here? open is already an outlier that only library authors will play with, and that's a good thing. It reduces the number of access level truly useful on a day to day basis, while leaving the others for more narrow purposes.

I would definitely prefer a set of easy to understand access levels over splitting modifiers in two groups, with one set reserved for "experts".
Access levels are a very basic concept, and imho they shouldn't need long explanations.

Strong -1. This is a source breaking change, but Swift 4 stage 2 is already over.

I agree with you here. I don't think the proposal will be accepted as is, because of the breaking change.

What if there was a way to make this a new feature so it is not a breaking change?

I can see this same proposal being accepted if it was revised to mark the new extension behavior explicitly.

I like this idea, only extensions explicitly marked as "privileged" can have access to private members of the type.
But I suggest then go forward and do not use extension to access the private members but instead introduce new construction, which also can have stored properties and will have access modifier rules for types (not the rules for extensions). And such construction will be treated as continued declaration of the type itself. Only in the same file for now.

For example (just as idea for discussion) :

struct A {
   private var x = 0
}

continue A: P1 {
   private var y = 10
   func foo() {print(x + y)}
}

continue A: P2 {
   func bar() {print(x + y)}
}

Probably such type also should be marked somehow to be allowed to have continued definition, like

struct A split {
}

Later, when we'll have submodules, such 'continue' construction for the type should be allowed in other files of the same submodule and provide the flexible way to define the same type in a number of files.

···

On 11.04.2017 19:47, Jose Cheyo Jimenez via swift-evolution wrote:

On Apr 6, 2017, at 4:25 PM, Slava Pestov via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

*Type.swift*
*
struct A {
private func foo() {}
}

*partial* extension A { // any other keyword would do here or even \
func baz() {
foo() // okay
}

extension A {
func boo() {
foo() // error. Same as swift 3.
}

*Other.swift*
*
*partial* extension A { // error. Partial extensions only allowed within the declaring file.
}

extension A {
func boo() {
foo() // error. Same as swift 3.
}

Slava

On Apr 6, 2017, at 4:10 PM, Douglas Gregor <dgregor@apple.com >>> <mailto:dgregor@apple.com>> wrote:

Hello Swift community,

The review of SE-0169 "Improve Interaction Between private Declarations and Extensions" begins now and runs through April 11, 2017. The proposal is available here:

    https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md

Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

    https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

    Proposal link:

        https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md

    Reply text

        Other replies

          <https://github.com/apple/swift-evolution/blob/master/process.md#what-goes-into-a-review-1&gt;What
          goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  * What is your evaluation of the proposal?
  * Is the problem being addressed significant enough to warrant a change to Swift?
  * Does this proposal fit well with the feel and direction of Swift?
  * If you have used other languages or libraries with a similar feature, how do
    you feel that this proposal compares to those?
  * How much effort did you put into your review? A glance, a quick reading, or an
    in-depth study?

More information about the Swift evolution process is available at

    https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Doug

Review Manager

_______________________________________________
swift-evolution-announce mailing list
swift-evolution-announce@swift.org <mailto:swift-evolution-announce@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution-announce

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

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

Using extensions for "code organization" is another word for writing spaghetti code.

Using extensions for code organization is a practice established and recommended by the Core Team from the very beginning (such as this blog post from August 2014: Swift Blog - Apple Developer).

Additionally, here's Chris Lattner himself in regards to extensions:

This design is true to the existing design of Swift: we want to encourage the implementation of types to be freely broken into extensions. This alignment with extension oriented programming was the one important virtue of the Swift 1/2 access control design that Swift 3 lost.

So while you personally may not use extensions, calling the use of them "spaghetti code" is rather misguided IMO.

Regardless, as a final plea to the Core Team regarding this proposal: unsurprisingly, the people who are on this mailing list represent a specific subset of Swift users, and not the Swift user base as a whole. The arguments against this proposal I truly believe are not relevant to the "average" Swift user.

A newcomer to Swift would probably begin by putting all logic for a class in one class declaration, and using "private" for private members (as is expected). However, once they find out about the Swift practice of using extensions to separate out logic, they will hit a roadblock as they will be unable to use private members as is, and rather than understand why, they very well may simply choose to never adopt an extension-based workflow, which is unfortunate. However, if this proposal was accepted, they could easily go from one class declaration to one with extensions, without even having to learn about fileprivate.

I truly believe this lessening of restrictions over private member in extensions (which is used by practically all levels of Swift programmers) is more important to the language than the ability to truly restrict types to just the original class scope (which I believe is primarily used by more "advanced" users). Experienced users know what they're doing, and can deal with shortcomings (such as prefixing truly private members with underscores). Newcomers/"average" users don't as much, and this friction is a much bigger deal for them. Also, while some have mentioned that this adds complexity to the access control system because types are now a factor, to that I say: this is purely a theoretical issue. In practice, developers don't care that this is case, and just care that the access control logic makes sense when using it. As someone who's had to explain why fileprivate is necessary many times to new Swift developers when using extensions, I can assure you that using this proposed private definition makes more intuitive sense to them.

To sum up, please don't lessen the experience of the average Swift programmer simply because there are some gains for the more "advanced" developers; obviously not everyone can be happy, but I believe in this case this proposal will satisfy the vast vast majority of Swift programmers (aka, people not on this mailing list).

Riley Testut

···

On Apr 16, 2017, at 1:02 PM, Rudolf Adamkovic via swift-evolution <swift-evolution@swift.org> wrote:

-1 from me.

Using extensions for "code organization" is another word for writing spaghetti code.

It results in types with many responsibilities. In such cases, it's time to extract collaborator types.

But it sure looks prettier.

R+

Sent from my iPhone

On 7 Apr 2017, at 10:56, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

What is your evaluation of the proposal?

Strong -1. Just rename ‘fileprivate’ to be less annoying.

This proposal will make things even worse than they are currently. We will regret it just as much, if not more than, 0025. As others have mentioned, it is actively harmful:
• It once again changes the meaning of private
• It takes away most of the actual power of private (vs fileprivate). (I was for returning to the simpler Swift 2 access, but when I did use private, I used it to limit access to just a few lines of code. This proposal gets rid of the last ounce of usefulness of ‘private’ for me, and only has the virtue of a less annoying name).
• It is the camel’s nose in the tent for type-based access (people will ask for future versions to be available in the type in the submodule, module, and then public… but we will be unable to give it to them)
• It breaks the same code that 0159 would have broken
• It will be a nightmare to teach/learn

Also, the idea that we should limit the use of ‘fileprivate’ is incorrect. Fileprivate is the best access levels for a lot of cases, it just has an annoying name. Given our constraints, I now believe the only sane choice left to us is to make fileprivate easier to use (as opposed to making private more like it) and to get rid of the cognitive dissonance of having similar names/concepts by renaming ‘fileprivate’ to something like ‘local’. ‘fileprivate’ (whatever it is called) should be the soft-default. ‘private’ should be the one being used explicitly.

We are likely going to have to rename ‘fileprivate’ anyway to work with submodules (that or add another access level), and ‘local’ has connotations of visible nearby… so I think it works well enough.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, access controls are a mess… but this will make them more of a mess.

Does this proposal fit well with the feel and direction of Swift?

No.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

No. I have used languages with type-based private, and I have used languages with file-based private… but never type-based that was limited to a file. You got shrimp in my chocolate (not all great tastes go well together).

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I have followed the discussion closely and spent a great deal of time thinking about it.

Thanks,
Jon

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

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

Reply below.

And it's a style that is so widely used by the Swift community that even if you think it's a bad practice does not change the fact that many people use that style and that the current access level rules don't play well with them.

Just because it’s widely used doesn’t mean it’s a good idea. Remember, Swift is still very young. SOLID and other “old school" OOP principles are way older and proven to work at this point. For example, as an iOS contractor, I very, very often see extensions used for “code organization” of massive view controllers. It’s easy to do and looks pretty. But that’s just putting lipstick on a pig. Of course, this is just anecdotal evidence and a personal opinion. By no means I’m saying I’m right. Just sharing my view and voting. I may be wrong of course, especially among very smart people that are here.

It results in types with many responsibilities. In such cases, it's time to extract collaborator types.

But it sure looks prettier.

R+

Sent from my iPhone

R+

···

On 17 Apr 2017, at 00:46, David Hart <david@hartbit.com> wrote:

You’re right, I overgeneralized. Let me correct myself:

From what I’ve seen so far [*], using extensions for "code organization" often results in spaghetti code.

[*] Observed as a contractor; iOS platform; average Joe's code.

Thanks!

R+

···

On 17 Apr 2017, at 01:46, Riley Testut <rileytestut@gmail.com> wrote:

So while you personally may not use extensions, calling the use of them "spaghetti code" is rather misguided IMO.

Also, while some have mentioned that this adds complexity to the access control system because types are now a factor, to that I say: this is purely a theoretical issue. In practice, developers don't care that this is case, and just care that the access control logic makes sense when using it. As someone who's had to explain why fileprivate is necessary many times to new Swift developers when using extensions, I can assure you that using this proposed private definition makes more intuitive sense to them.

Well, I guess than it would be the best to ignore private completely and only teach the model of Swift 2:
Without private, access control is completely separated from from extensions (mixing concerns rarely makes things easier), and you save yourself from the questions why extension A can't see a property declared five lines above, wheres extension B at the bottom of the same file has no such problem.
No one really knows what's better for the "average Swift programmer", and no matter how many newcomers you have met, I doubt it is a significant share of all Swift users out there.

Sticking to facts, we have a steady increase of access control complexity, and this is pepped by adding special rules to work around perceived or real inconveniences.
SE-0169 aims to be a small change with minimal impact, but those pile up easily...

I would like to see a much more coherent and simple access control system
in Swift. And since the proposal to revert `private` to what it was before
was rejected, it is better that` private` means really `private`. Then -1
for this proposal.

(I think we could learn a lot from C # in this question [and others too],
anyway.)

— Van

Partials from a C# sense only really shine when they are cross-file, same module. Then, preprocessing rules can turn something like a language description, XML-based language or extended HTML markup into property definitions and template rendering code. A type is partially user code, partially generated code.

Other languages may support this via the preprocessor-generated code being a super type or subtype of the user code type, but then you necessarily aren’t a peer - either a super type preprocessor class can’t see the user methods to hook in things like actions and actions, or the user class can’t access properties and methods declared by the preprocessor. In both cases, the root type usually winds up being declared abstract.

I don’t know what a same-file partial would be, other than an extension which also contributes to storage requirements of the type.

-DW

···

On Apr 9, 2017, at 7:04 PM, Jose Cheyo Jimenez via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 8, 2017, at 5:19 AM, Neil via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I agreed with Charlie, but I think there’s another option.

The access control problems that both SE-0159 and SE-0169 are attempting to address can be resolved not by changing the definition of the existing access modifiers, but refocussing the use of extensions.

One such way would be to introduce a `partial` modifier. It would be similar to C#’s partial modifier. The proposed partial modifier would be purely additive and it would go a long way to mitigate the access control issues inherent in extension-oriented design.

The key characteristics of partial-oriented design would be:

- Partials would allow the splitting of implementations into multiple logical units without the same-file restriction.

I really like the idea of partial but it would need to be restricted to the same file. This would allow partial to just be a compile time feature which just zips up a type together. Another great feature would be that it could allow stored properties because it is only a compile time feature. Partial could be used to communicate to the user that this type has additional members and properties declared somewhere else in the file.

+1 for file scope partial.

FWIW, there is a temporal aspect to this. 1-2 weeks ago, I was more open to the discussion about renaming fileprivate. However, as of the core team meeting last week, it became clear that the priority of maintaining source stability (and thus, not massively thrashing source files in the 3->4 conversion) is an overriding concern.

As such, it is clear that changing fileprivate isn’t going to happen. This is the direct sequence of events that led to 0169 being proposed.

-Chris

···

On Apr 10, 2017, at 12:28 PM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

Speaking just for myself, I don't think we'd accept such a change purely for aesthetics;

If I recall correctly, Chris in post-review discussions communicated that a new keyword to replace `fileprivate` would be considered if `fileprivate` turned out to be commonly used enough to be aesthetically problematic?

Yes. We would consider it. Chris probably believes that we're more likely to accept that change than I do; that's why I was careful to say that I was just speaking for myself. (Chris didn't need to say that explicitly in his message because he wasn't explicitly speaking for the Core Team in other parts.)

And it's a style that is so widely used by the Swift community that even if you think it's a bad practice does not change the fact that many people use that style and that the current access level rules don't play well with them.

Just because it’s widely used doesn’t mean it’s a good idea. Remember, Swift is still very young. SOLID and other “old school" OOP principles are way older and proven to work at this point. For example, as an iOS contractor, I very, very often see extensions used for “code organization” of massive view controllers. It’s easy to do and looks pretty. But that’s just putting lipstick on a pig. Of course, this is just anecdotal evidence and a personal opinion. By no means I’m saying I’m right. Just sharing my view and voting. I may be wrong of course, especially among very smart people that are here.

I share your concerns: There is no proof that using extensions to group protocol-methods has any benefit, and there is no proof for the contrary.
Looking at the status quo, we see encouragement for this practice from the creators of Swift, which is merely copied by authors of styleguides and finally consumed by the "average coder".
So, is it that surprising that many people adopt that concept?

SE-0169 is based on little more that than a gut feeling how code should be organised — and even the smartest minds can't always trust their intuition (I hope they won't disagree with this claim ;-).

You’re right, I overgeneralized. Let me correct myself:

From what I’ve seen so far [*], using extensions for "code organization" often results in spaghetti code.

[*] Observed as a contractor; iOS platform; average Joe's code.

Fair :-)

Another use case for extensions I had forgotten about that I have used extensively, however, is using to group private or public members together, and have the access control level of the extension apply to each member in the extension (by default). Typically, I’ll declare the properties and initializers in the main type declaration, and then all public methods I’ll place in a “public” extension, and all private methods I’ll place in a “private” extension. This IMO is a great win for code readability, and another thing that I have lost the ability to do without using file private.

···

On Apr 17, 2017, at 1:04 AM, Rudolf Adamkovič <salutis@me.com> wrote:

On 17 Apr 2017, at 01:46, Riley Testut <rileytestut@gmail.com> wrote:

So while you personally may not use extensions, calling the use of them "spaghetti code" is rather misguided IMO.

You’re right, I overgeneralized. Let me correct myself:

From what I’ve seen so far [*], using extensions for "code organization" often results in spaghetti code.

[*] Observed as a contractor; iOS platform; average Joe's code.

Thanks!

R+

All right, time to dive in!

First things first, the “helper visible” row in the table I posted is
actually unnecessary: a private helper type can have its visible members
unmarked (so, “internal”) and they will be available throughout the file.

Now, if we believe that cross-type sharing ought be avoided, then the
primary place “fileprivate” should occur today is when extending a type in
the same file. The one other time it might be unavoidable is when extending
an existing type to have an initializer that takes the new type. Thus in
“Foo.swift” we might have “extension Bar { init(foo: Foo) … }” and need to
see hidden details of Foo.

Similarly, if we think that unrelated types do not belong together in the
same file, then the bottom two rows (which deal with multi-type files) can
be expunged as well. After all if there are two types which must not have
privileged access to one another, they should go in separate files.

Thus the only place where “private” need appear today is when protecting
members of a type from the rest of that same type. However, as has been
mentioned on-list, “private” alone does not achieve sufficient
encapsulation for this purpose, because a private variable can be seen in
all functions located in the main type declaration.

Moreover, if a type has two separate invariants, and dedicated methods for
interacting with them, the only way to hide one invariant from the other’s
methods is to use helper types. It seems that “use a private helper type”
should be the preferred way to protect invariants, and if the helper type
needs to see the rest of the type’s members then it should be nested.

Putting everything together, an updated version of the table looks like
this, where the “Privileged init” row refers to the scenario described
earlier:

*No change*

*SE–0159*

*SE–0169*

*Rename*

*Simple file*

private

private

private

private

*Extensions*

fileprivate

private

private

private

*Privileged init*

fileprivate

private

fileprivate

private

*Helper hidden*

private

no hiding

private

scoped

*Invariants*

helper type

no hiding

helper type

helper type

*No change*

If we do not make a change, then we will be stuck using “fileprivate” in
perpetuity. This might be a purely aesthetic concern, but I would liken it
to a splinter in the finger. Yes it is small, but it hurts every time we
touch something with it, and it will keep hurting until we yank it out.

The existing meaning for “private” is really only useful to protect members
of a helper type. It should not be used for any other purpose because it
hamstrings the ability to add extensions, and it is insufficient for true
encapsulation outside of helper types.

*SE–0159*

If Swift takes the opinionated stance that one should not put things that
need to be hidden from each other in the same file, then SE–0159 is the
clear winner. After all, if you can’t hide things from other parts of the
file at all, then you won’t be tempted to try and thus you will keep your
separate types in separate files. This will also simplify the access
control model, making it easier to reason about code and decide which
visibility to use.

It will cause churn, however, as existing projects that use sub-file-level
hiding must adapt. In the future a submodule system with a visibility level
between “private” and “internal” (perhaps “protected”?) could re-enable the
use of helper types (in separate files!) to preserve invariants.

*SE–0169*

If we accept the current proposal, then we will only really need
“fileprivate” when extending another type to add something like an
initializer which requires privileged access to the main type in a file.
Additionally, people will be encouraged to use helper types for
encapsulating invariants, because the scope-based “private” of today will
no longer be a tempting-but-inferior alternative.

This option also causes churn, though perhaps in a good way as projects
which use “private” for encapsulation must switch to the superior design of
helper types. Furthermore, a private helper type can be nested in an
extension, so its implementation need not occupy space within the main type
declaration. It is worth noting than a private nested helper type cannot be
extended, and is thus guaranteed to be defined entirely within a single
code block, because it is only visible in the outer type and extensions
cannot be nested.

The access control model becomes more complex to explain, though perhaps
simpler to understand. However, one concern is that SE–0169 might
effectively encourage people to place unrelated types in the same file.
After all, one might reason, why would they have carved out this
weirdly-specific meaning for “private” if they didn’t expect and intend for
me to put several different types in a single file?

*Rename*

If we change the spelling of “private” to “scoped” and of “fileprivate” to
“private”, then there will be no extra work for developers because the
semantics are identical and migration can be automated. This is the
solution of least churn.

It also means that “private” can be used everywhere except for invariants
within helper types, and that is a very good thing. If invariants are
marked “scoped”, and nothing else is, then any change away from “scoped” is
easy to spot in code review.

This approach leaves the possibility that people will try to encapsulate
with just “scoped” and not use a helper type, which should be addressed in
style guides. However it also discourages people from putting unrelated
types in the same file, because “private” does not have exceptions carved
out that indicate otherwise.

In the future when we get submodules, then helper types can go in their own
files and a discussion on removing “scoped” may take place. That will get
us a lot more simplicity, and it will avoid the temporary inability to
encapsulate invariants that SE–0159 would bring.

*Conclusion*

The simplicity of SE–0159 is admirable, but the desire for encapsulation of
invariants rules it out. Making no change is unacceptable because we would
be stuck with “fileprivate”, so the question comes down to SE–0169 and
renaming.

As I see it, renaming is the superior option. It brings less churn because
semantics don’t change and migration can be fully automated. It removes
“fileprivate” altogether, whereas SE–0169 keeps it around. And renaming
avoids the problematic implication of expectations whereby SE–0169 silently
encourages people to put unrelated types in one file.

The fact that SE–0169 is apparently designed specifically to shield types
from each other within the same file will inevitably lead people to use it
for exactly that purpose.

During the discussion of SE–0159, a large number of people on both sides of
the issue said they would support renaming to “private” and “scoped”. As an
option which appeals to developers regardless of whether they use
sub-file-level encapsulation, as an option which does not blur the lines
about what constitutes a scope, and as an option which preserves all the
semantics of our existing access levels, I think that renaming is the best
way to solve the “fileprivate” problem.

The fact that it exactly matches the original intent of SE–0025 is an added
bonus.

Nevin

In order to evaluate this proposal, I created a table of as many relevant
scenarios as I could think of, and listed how each of the proposed
solutions would work there. The four access level systems I compared are:

*No change:* The existing Swift 3 *status quo* with “fileprivate” and
“private”.

*SE–0159:* Revert to the Swift 2 meaning of “private” (file visibility) and
eliminate scoped access altogether.

*SE–0169:* Expand “private” to include the current type and all its
extensions in the same file.

*Rename:* Change the spelling of “private” to “scoped”, and “fileprivate”
to “private”.

I ended up with a list of eight scenarios to evaluate. These are:

1. “Simple file”, a single type with no extensions.
2. “Extensions”, a single type and extensions thereof.
3. “Sharing”, multiple types that need privileged access to each other.
4. “Helper visible”, the interface of a helper (or nested) type for use
within the file.
5. “Helper hidden”, the details of a helper type that should not be visible
to other types.
6. “Invariants”, the details of a type that should only be touched by
dedicated methods.
7. “Multi-type”, multiple types that should not have privileged access to
each other.
8. “Multi-type + ext”, multiple types that should not have privileged
access to each other, and extensions thereof.

The entries in the table have seven possible values:

*private:* The “private” keyword works.

*fileprivate:* The “fileprivate” keyword works.

*scoped:* The “scoped” keyword works.

*private x2:* The “private” keyword works if the types are put into
separate files.

*fileprivate x2:* The “fileprivate” keyword works if the types are put into
separate files.

*helper type:* The goal can only be achieved by creating a helper type.

*no hiding:* The specified items cannot be hidden from the rest of the file.

Here is the completed and color-coded table (I hope it displays properly in
this email):

*No change*

*SE–0159*

*SE–0169*

*Rename*

*Simple file*

private

private

private

private

*Extensions*

fileprivate

private

private

private

*Sharing*

fileprivate

private

fileprivate

private

*Helper visible*

fileprivate

private

fileprivate

private

*Helper hidden*

private

no hiding

private

scoped

*Invariants*

private

no hiding

helper type

scoped

*Multi-type*

private

private x2

private

scoped

*Multi-type + ext*

fileprivate x2

private x2

private

private x2

*Analysis*

Some people have said on-list that they view cross-type sharing as an
anti-pattern. If that is true, then we can simply ignore the “Sharing” row
because it would not be worth optimizing for. This hardly changes the table
though, as that row is identical to the “Helper visible” row below it.

Regarding the “Invariants” row, note that a helper type can be used for
this purpose under “No change” and “Rename” just as well as under
“SE–0169”. The difference is that SE-0169 provides no other way to hide
invariants from the rest of the type, whereas the other two have an access
level suited to the purpose. Similarly, the multi-type rows can be
satisfied by separate files regardless of which access system is in place.

If a person takes the view that things in the same file should naturally
have access to one another, then the bottom four rows of the table can be
ignored. Such a person might have the mantra, “Only put things together if
they belong together”, and they would ask, “What else is in the file that
you are trying to hide from?”

For someone whose primary use-case is to put one type in a file and build
it up through extensions, the “Extension” row is of greatest concern. To
them, any of SE–0159, SE–0169, or renaming would let them use “private”
instead of “fileprivate” everywhere.

As a final remark, each of the four options has one notable benefit and one
notable drawback, not all of which are apparent from the above table:

*No change*
Benefit: No change to the language.
Drawback: Must use “fileprivate” to build up a type through extensions.

*SE–0159*
Benefit: Simplifies the access control model.
Drawback: Cannot protect invariants within a file.

*SE–0169*
Benefit: Cross-type sharing is clearly marked.
Drawback: Must use a helper type to protect invariants within a file.

*Rename*
Benefit: No change to semantics.
Drawback: Two separate keywords are changed.

• • •

And now, having said all that, I need to go take a nap!

Once everything has percolated, I’ll come back to write a review of the
current proposal.

Nevin