[Discussion] fileprivate vs. private(file)


(Haravikk) #1

So discussion of the exact naming convention for access modifiers has been discussed multiple times, especially when fileprivate was being introduced. It's now been a while since fileprivate was added, so we've all had more time to actually work with it, so I wanted to raise the discussion again to see what people now think.

Specifically, during the debate over access modifiers I really didn't like the propose fileprivate, as I don't like conjoined lower-case key-"words" (to me a keyword should always be a single word, I still don't like associatetype or typealias either, and neither does macOS' auto-correct :wink:). And to be honest my opinion hasn't changed; if anything I hate it even more.

The proposal I preferred was to use only the public and private keywords and use parameters to provide greater specificity, like so:

  public as it is now
  private(module) equivalent to internal, which is private to the current module/project
  private(file) equivalent to fileprivate
  private(scope) equivalent to current private

The actual parameters are up for debate, but I still feel this a lot clearer. I also generally prefer the parameterised version for several reasons:

Readability: I know not everyone will agree on this one, but I much prefer the separation to having conjoined keywords.
Fewer Keywords: Maybe I'm a keyword purist or something, but I just don't like having too many keywords in the language, especially four for what amounts to a single feature (access).
Extensibility: It is easier and cleaner to add more parameters in future. For example, private(type) could be an equivalent to protected access in other languages. It can also be used to more easily group external extension modifiers under public such as public(final), public(open) etc.

For setter-specific access it might look something like:

  private(file:set) var someValue:Int = 0

I know it might not seem that important to some people, but this is one area of Swift's syntax that still bothers me, perhaps unreasonably so, but I don't get to pick what annoys me! I'd also seek to replace associatedtype and typealias (and any other conjoined keyword I've forgotten) but I haven't thought of any alternatives that I like for those yet.

Anyway, just wanted to discuss how others felt having used fileprivate and such for a while, and whether it's worth revisiting, or if I just have to live with it.


(Brent Royal-Gordon) #2

We considered this last year and decided it was a bad idea. The `private` keyword ends up just being surplusage—it just adds noise without adding meaning.

I think you may be forgetting that we're now under source stability constraints. It's one thing to drop ill-advised features or rename keywords to make space for new features; it's something else to rename things all across the language for the sake of renaming them.

···

On Feb 20, 2017, at 3:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

The proposal I preferred was to use only the public and private keywords and use parameters to provide greater specificity, like so:

  public as it is now
  private(module) equivalent to internal, which is private to the current module/project
  private(file) equivalent to fileprivate
  private(scope) equivalent to current private

--
Brent Royal-Gordon
Architechies


(Ross O'Brien) #3

Let's start with: I much prefer including the word 'file' in the file
access level, to not including it. The bikeshedding discussions proposed
too many permutations of 'public' 'external' 'internal' 'private' for me,
not to mention the potential additions of 'secret', 'hidden', 'closed'... I
think single-word adjectives would be nice if there was a ready hierarchy
available in English, but there isn't, and inventing one for this purpose
makes learning and readability difficult.

So, burrowing back into the bike shed...

I think the three features you've listed - readability, fewer keywords,
extensibility - work within the dimension of access levels we have now, as
long as private(module) is implicitly the default. I'm curious about the
setter-specific access where that isn't the case, though - suppose the
getter-specific access level for your example wasn't 'internal'. How would
we declare this?

public(open: get), private(file: set) var someValue: Int = 0
private(file: get, scope: set) var someValue: Int = 0

Orthogonal extensions, such as 'protected', multiply the problem. How do
you restrict which access levels get 'type' access?

I think perhaps you reverse the arguments:
private(set: scope, get: file, type: module)

Perhaps, by default:

These four are equivalent:
var someValue: Int = 0
private var someValue: Int = 0
private(module) var someValue: Int = 0
private(get: module) var someValue: Int = 0

'set' defaults to 'get' level unless explicitly restricted further, e.g.
these two are equivalent
private(set: file) var someValue: Int = 0
private(get: module, set: file) var someValue: Int = 0

Perhaps 'type' access is 'set' level unless explicitly exposed:
private(get: file, set: scope, type: module)

... It doesn't make sense to me that 'type' would be more restricted than
'set', but offhand I haven't figured out if 'type' can be more exposed than
'get'. If there are restrictions like this the compiler can complain that
the access levels specified are inconsistent.

This still seems a bit clunky to me if you have a public and a private on
the same line, but perhaps someone can suggest something for that.

Ross

···

On Mon, Feb 20, 2017 at 11:42 AM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

So discussion of the exact naming convention for access modifiers has been
discussed multiple times, especially when fileprivate was being introduced.
It's now been a while since fileprivate was added, so we've all had more
time to actually work with it, so I wanted to raise the discussion again to
see what people now think.

Specifically, during the debate over access modifiers I really didn't like
the propose fileprivate, as I don't like conjoined lower-case key-"words"
(to me a keyword should always be a single word, I still don't like
associatetype or typealias either, and neither does macOS' auto-correct
:wink:). And to be honest my opinion hasn't changed; if anything I hate it even
more.

The proposal I preferred was to use only the public and private keywords
and use parameters to provide greater specificity, like so:

public as it is now
private(module) equivalent to internal, which is private to the current
module/project
private(file) equivalent to fileprivate
private(scope) equivalent to current private

The actual parameters are up for debate, but I still feel this a lot
clearer. I also generally prefer the parameterised version for several
reasons:

   - Readability: I know not everyone will agree on this one, but I much
   prefer the separation to having conjoined keywords.
   - Fewer Keywords: Maybe I'm a keyword purist or something, but I just
   don't like having too many keywords in the language, especially four for
   what amounts to a single feature (access).
   - Extensibility: It is easier and cleaner to add more parameters in
   future. For example, private(type) could be an equivalent to protected
   access in other languages. It can also be used to more easily group
   external extension modifiers under public such as public(final),
   public(open) etc.

For setter-specific access it might look something like:

private(file:set) var someValue:Int = 0

I know it might not seem that important to some people, but this is one
area of Swift's syntax that still bothers me, perhaps unreasonably so, but
I don't get to pick what annoys me! I'd also seek to replace associatedtype
and typealias (and any other conjoined keyword I've forgotten) but I
haven't thought of any alternatives that I like for those yet.

Anyway, just wanted to discuss how others felt having used fileprivate and
such for a while, and whether it's worth revisiting, or if I just have to
live with it.

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


(Haravikk) #4

The proposal I preferred was to use only the public and private keywords and use parameters to provide greater specificity, like so:

  public as it is now
  private(module) equivalent to internal, which is private to the current module/project
  private(file) equivalent to fileprivate
  private(scope) equivalent to current private

We considered this last year and decided it was a bad idea. The `private` keyword ends up just being surplusage—it just adds noise without adding meaning.

It may not add a lot of meaning as such, but personally I find it a lot cleaner; everything other than public is a form of privacy by module, file etc., so it makes a lot of sense to me to have them grouped in that way. The current keywords are also inconsistent; we have public, private, internal and fileprivate, only one of these is a conjoined keyword, and internal isn't any less a form of privacy than private and fileprivate.

I also disagree that it adds noise; to me I find the above easier to read, as private tells me that the property/method is being restricted in some way, and I can look at the parameters to find out more (if there are any). Bear in mind that most of the time developers will still only use public, private and nothing at all (the private(module) default), they only need to add the parameter when something more unusual is required.

In fact, it's one argument for the reduction in keywords, as in my experience I very rarely see anyone actually use internal, usually they just omit the keyword, so most of the time it is a waste to define it, and internal might be nice to have available as a variable name rather than reserved as a keyword that most people rarely, if ever, use. Part of my concern is that there are a number of proposals on the list at the moment that may seek to add new keywords, so trimming this down is useful. It's also a very easy change to migrate; fileprivate becomes private(file), internal becomes private(module).

I'd also argue that private(module) is clearer in meaning than internal, and private(scope) is much clearer in meaning that just private, though I'm currently undecided of which should be the default for private without parameters.

it's something else to rename things all across the language for the sake of renaming them.

I think it's unfair to say it's just "for the sake of renaming them"; I feel it's a more consistent and more future proof scheme that cuts down on the number of keywords required, and I also find it easier to read.

···

On 20 Feb 2017, at 13:30, Brent Royal-Gordon <brent@architechies.com> wrote:

On Feb 20, 2017, at 3:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:


(Haravikk) #5

Orthogonal extensions, such as 'protected', multiply the problem. How do you restrict which access levels get 'type' access?

Well, I was really just mentioning it as a future possibility, I hadn't quite gotten as deciding exactly how type would be used. It's still a form of privacy so I expect it would be grouped under private(type), with the caveat that if the type is public then any private(type) methods and properties will be available to external sub-classes.

Perhaps, by default:

These four are equivalent:
var someValue: Int = 0
private var someValue: Int = 0
private(module) var someValue: Int = 0
private(get: module) var someValue: Int = 0

'set' defaults to 'get' level unless explicitly restricted further, e.g. these two are equivalent
private(set: file) var someValue: Int = 0
private(get: module, set: file) var someValue: Int = 0

Perhaps 'type' access is 'set' level unless explicitly exposed:
private(get: file, set: scope, type: module)

... It doesn't make sense to me that 'type' would be more restricted than 'set', but offhand I haven't figured out if 'type' can be more exposed than 'get'. If there are restrictions like this the compiler can complain that the access levels specified are inconsistent.

This still seems a bit clunky to me if you have a public and a private on the same line, but perhaps someone can suggest something for that.

This is what I had in mind; i.e- you don't have to parameterise private, you'd only do it if you want something other than the default.

As for something with both public and private modifiers (only really applies to properties I think?) I think it's fine to just declare each separately, as there may in future be more public parameters so it makes sense to have the separate grouping of a property's public and private components.

I'm not clear on your use of private(type:module), what do you see that as representing? Personally I'd just expect them to be used like private(module, type), i.e- the method/property is private but accessible by both module and (externally) by sub-classes.

···

On 20 Feb 2017, at 12:27, Ross O'Brien <narrativium+swift@gmail.com> wrote:

On Mon, Feb 20, 2017 at 11:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
So discussion of the exact naming convention for access modifiers has been discussed multiple times, especially when fileprivate was being introduced. It's now been a while since fileprivate was added, so we've all had more time to actually work with it, so I wanted to raise the discussion again to see what people now think.

Specifically, during the debate over access modifiers I really didn't like the propose fileprivate, as I don't like conjoined lower-case key-"words" (to me a keyword should always be a single word, I still don't like associatetype or typealias either, and neither does macOS' auto-correct :wink:). And to be honest my opinion hasn't changed; if anything I hate it even more.

The proposal I preferred was to use only the public and private keywords and use parameters to provide greater specificity, like so:

  public as it is now
  private(module) equivalent to internal, which is private to the current module/project
  private(file) equivalent to fileprivate
  private(scope) equivalent to current private

The actual parameters are up for debate, but I still feel this a lot clearer. I also generally prefer the parameterised version for several reasons:

Readability: I know not everyone will agree on this one, but I much prefer the separation to having conjoined keywords.
Fewer Keywords: Maybe I'm a keyword purist or something, but I just don't like having too many keywords in the language, especially four for what amounts to a single feature (access).
Extensibility: It is easier and cleaner to add more parameters in future. For example, private(type) could be an equivalent to protected access in other languages. It can also be used to more easily group external extension modifiers under public such as public(final), public(open) etc.

For setter-specific access it might look something like:

  private(file:set) var someValue:Int = 0

I know it might not seem that important to some people, but this is one area of Swift's syntax that still bothers me, perhaps unreasonably so, but I don't get to pick what annoys me! I'd also seek to replace associatedtype and typealias (and any other conjoined keyword I've forgotten) but I haven't thought of any alternatives that I like for those yet.

Anyway, just wanted to discuss how others felt having used fileprivate and such for a while, and whether it's worth revisiting, or if I just have to live with it.


(Ross O'Brien) #6

This is what I had in mind; i.e- you don't *have* to parameterise
private, you'd only do it if you want something other than the default.

As for something with both public and private modifiers (only really
applies to properties I think?) I think it's fine to just declare each
separately, as there may in future be more public parameters so it makes
sense to have the separate grouping of a property's public and private
components.

I'm not clear on your use of private(type:module), what do you see that as
representing? Personally I'd just expect them to be used like
private(module, type), i.e- the method/property is private but accessible
by both module and (externally) by sub-classes.

Does 'private(module, type)' grant get-set access to external subclasses,
or get-only?

If I write 'private(file, type)' is the property accessible only to types
within the module, or outside as well?

The 'type' axis (which includes the 'protected' concept) is related to the
'module' axis (which includes 'internal' and 'fileprivate') and the 'get
set' axis. Perhaps not strictly orthogonal, but still three dimensions. If
Swift is to add a type axis, we should be careful not to think of it as
simply additional states on the 'module' axis.


(Haravikk) #7

Does 'private(module, type)' grant get-set access to external subclasses, or get-only?

Without a specific set condition it'd be both get and set for both. So you might need to do for example:

private(module, set:type) func someMethod() { … }

Though this may be assuming that set always implies get; I'm not sure if there's a need for set-only access restrictions, as any example I can think of that might need it can be handled by a method specifically for that purpose.

If I write 'private(file, type)' is the property accessible only to types within the module, or outside as well?

The property would be accessible anywhere in the same file, as well as accessible to any sub-class both in the module and externally.

Are you asking whether it'd be possible to restrict the external access of type? This might be an argument for allowing the type parameter on both private and public perhaps? So if you want external access to sub-classes you'd do:

  public(type) func someMethod() { … }

Whereas for internal access you'd do:

  public(file, type) func someMethod() { … }

Or are there still cases that aren't covered? It's possible it's a bad example of something that can be added to the parameterised form in future, and maybe it is something that does still require its own keyword?

···

On 20 Feb 2017, at 15:25, Ross O'Brien <narrativium+swift@gmail.com> wrote:


(Xiaodi Wu) #8

I'm terribly sorry to be _that guy_, but I have to interject that
`private(file)` was discussed extensively during review for SE-0025, that
the full scheme laid out in the first message of this thread was given
consideration, and that the core team took these suggestions into account
before accepting and implementing `fileprivate`.

Now, it has been said that real-world experience opens the door to
reconsidering the substantive content of SE-0025 (as in, whether "new"
private should exist at all). It has also long been said that a superior
name would be considered instead of `fileprivate` if someone came up with
one. But clearly, if hundreds of messages advocating for `private(file)`
did not cause the core team to consider it to be a superior name, it is
exceedingly poor form to keep bringing it up again. It's already exhausting
to bikeshed upcoming features, but this shed has already shipped.

···

On Mon, Feb 20, 2017 at 11:14 AM, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

On 20 Feb 2017, at 15:25, Ross O'Brien <narrativium+swift@gmail.com> > wrote:
Does 'private(module, type)' grant get-set access to external subclasses,
or get-only?

Without a specific set condition it'd be both get and set for both. So you
might need to do for example:

private(module, set:type) func someMethod() { … }

Though this may be assuming that set always implies get; I'm not sure if
there's a need for set-only access restrictions, as any example I can think
of that might need it can be handled by a method specifically for that
purpose.

If I write 'private(file, type)' is the property accessible only to types
within the module, or outside as well?

The property would be accessible anywhere in the same file, as well as
accessible to any sub-class both in the module and externally.

Are you asking whether it'd be possible to restrict the external access of
type? This might be an argument for allowing the type parameter on both
private and public perhaps? So if you want external access to sub-classes
you'd do:

public(type) func someMethod() { … }

Whereas for internal access you'd do:

public(file, type) func someMethod() { … }

Or are there still cases that aren't covered? It's possible it's a bad
example of something that can be added to the parameterised form in future,
and maybe it is something that does still require its own keyword?

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


(Karl) #9

We are seeing lots of topics about access control in general. It’s clear people aren’t completely satisfied, but I don’t have a clear, coherent picture of what people dislike the most.

Personally, I just don’t like fileprivate, just like I didn’t like it’s ol’ pap, file-scoped “private”. Especially now we don’t have header files, I sometimes want to restrict access of a member to certain other types without making them exposed throughout the entire module (so the rest of my internal code is written in the closest thing to the client’s experience). At the same time, sticking everything in to one enormous file can sometimes be hard to manage - I end up with quarter-screens full of whitespace to act as visual separators.

I don’t know exactly what to suggest, but I feel we need to organise and answer the general grievances about access control and update the FAQ.

- Karl

···

On 21 Feb 2017, at 01:00, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I'm terribly sorry to be _that guy_, but I have to interject that `private(file)` was discussed extensively during review for SE-0025, that the full scheme laid out in the first message of this thread was given consideration, and that the core team took these suggestions into account before accepting and implementing `fileprivate`.

Now, it has been said that real-world experience opens the door to reconsidering the substantive content of SE-0025 (as in, whether "new" private should exist at all). It has also long been said that a superior name would be considered instead of `fileprivate` if someone came up with one. But clearly, if hundreds of messages advocating for `private(file)` did not cause the core team to consider it to be a superior name, it is exceedingly poor form to keep bringing it up again. It's already exhausting to bikeshed upcoming features, but this shed has already shipped.


(Sean Heber) #10

It might have been suggested (this discussion is huge), but I think private could just be defined more or less like a combination of Swift 3 fileprivate and private with one little twist - it is also accessible from extensions in other files internal to the defining module.

Module 1 File A.swift:

public class Foo {
  private var property: Int
}

class Bar {
  // a Foo's property could be accessible inside here
}

extension Foo {
  // property is accessible
}

Module 1 File B.swift:

// a Foo's property is NOT accessible

extension Foo {
  // property is accessible in internal extension
}

Module 2 File C.swift:

// a Foo's property is NOT accessible

extension Foo {
  // property is STILL NOT accessible here
}

l8r
Sean

···

Sent from my iPad

On Feb 20, 2017, at 6:22 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 21 Feb 2017, at 01:00, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

I'm terribly sorry to be _that guy_, but I have to interject that `private(file)` was discussed extensively during review for SE-0025, that the full scheme laid out in the first message of this thread was given consideration, and that the core team took these suggestions into account before accepting and implementing `fileprivate`.

Now, it has been said that real-world experience opens the door to reconsidering the substantive content of SE-0025 (as in, whether "new" private should exist at all). It has also long been said that a superior name would be considered instead of `fileprivate` if someone came up with one. But clearly, if hundreds of messages advocating for `private(file)` did not cause the core team to consider it to be a superior name, it is exceedingly poor form to keep bringing it up again. It's already exhausting to bikeshed upcoming features, but this shed has already shipped.

We are seeing lots of topics about access control in general. It’s clear people aren’t completely satisfied, but I don’t have a clear, coherent picture of what people dislike the most.

Personally, I just don’t like fileprivate, just like I didn’t like it’s ol’ pap, file-scoped “private”. Especially now we don’t have header files, I sometimes want to restrict access of a member to certain other types without making them exposed throughout the entire module (so the rest of my internal code is written in the closest thing to the client’s experience). At the same time, sticking everything in to one enormous file can sometimes be hard to manage - I end up with quarter-screens full of whitespace to act as visual separators.

I don’t know exactly what to suggest, but I feel we need to organise and answer the general grievances about access control and update the FAQ.

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