[swift-evolution] Replace Fileprivate with Hidden + Import Hidden

Jonathan Hull jhull at gbis.com
Sun Oct 16 22:25:08 CDT 2016


> On Oct 16, 2016, at 7:23 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> 
> On that last point, I would push back against calling it access inflation. If a member needs to be visible outside the file for whatever reason, it needs to have at least internal visibility. This is not at all "inflated" (implying that it's a workaround) but an explicitly contemplated part of Swift's access scheme.
But what happens when someone needs to make an extension outside of the module?  For example, subclassing UIGestureRecognizer.  Should things like setState: (which triggers a bunch of side effects and should never be called externally) be made public?  The only two options in pure Swift 3 (it only gains it’s protected-style import from ObjC) are to make them public or to disallow subclassing outside the module (you have to call setState: as part of implementing a custom gesture recognizer).


> What you're arguing is that we should be able to expressly enumerate the specific files in which a member is accessible. This is a more granular system, but increased granularity is not in and of itself sufficient justification. After all, if more granularity is better--and this is an argumentum ad absurdum--why not demand that we be able to expressly enumerate the specific types that can access that member? why not the specific methods of those types? why not the specific scope? why not the specific line?
Actually, I am arguing almost the opposite.  I had originally wanted a more granular system like 'protected’ from most languages.  This proposal comes from a realization that file granularity is most likely good enough for real world use cases.

What I am arguing is important:
1) The author’s ability to show which items should generally not be accessible/used, but are still provided/required for extension/subclassing purposes.
2) Requiring users of these items to acknowledge the author’s intent (by requiring ‘import hidden’) before using them
3) Terminology which has the correct connotation for this new behavior


> It's all about what use cases are enabled or not at different points along this spectrum of granularity. So the question is, does your proposed scheme enable additional use cases that `internal` does not?
It allows use of ‘hidden’ variables/functions from other modules.  It also allows the author to show their intention that the variable/function is meant to be accessed elsewhere if needed for extension (vs. internal which is the default)… and it requires the user to acknowledge that intention.


> I'm not convinced that having access to a member at a use site when the declaration itself is totally under your own control, in the same module, is "brittle" is any meaningful sense.
What I mean by brittle is how much a change in one spot will ripple throughout your code by requiring other changes.  It becomes a much larger issue when multiple people are working on a code base.


Thanks,
Jon

> On Mon, Oct 17, 2016 at 09:51 Jonathan Hull via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> To put it more forcefully, I believe that ‘fileprivate’ currently suffers from the same problem that singletons do.  That is, the main reason to make something ‘fileprivate’ instead of ‘private’ is to allow some sort of extension.  It is highly unlikely that the point you were required to expose for that extension will not also be needed by other extensions.  Both ‘internal’ and submodules would extend the boundaries of the problem from the file boundary to the module/submodule boundary… but the underlying problem still persists.
> 
> If it is the module designer’s intent to limit extension, then fine, but it also often forces a design to be much more brittle and fragile than it needs to be.  One of two things happens: extension becomes impossible or there is access inflation, giving inappropriate levels of access (usually combined with a note in the documentation saying not to use it).
> 
> Thanks,
> Jon
> 
> 
> 
>> On Oct 16, 2016, at 3:28 PM, T.J. Usiyan <griotspeak at gmail.com <mailto:griotspeak at gmail.com>> wrote:
>> 
> 
>> I don't like this at all and it comes down to  "what is hidden can also be unhidden". This, to me, feels like it would create more confusion than it would address. Why not just use `internal` for `hidden` items?  If we're ok with modifying import statements, why not simply have a command that imports `fileprivate` stuff? (not advocating for this).
>> 
>> I think that submodules would have really helped with this issue and it is unfortunate that we couldn't get them in for swift 3. 
>> 
>> On Sun, Oct 16, 2016 at 4:34 PM, Jonathan Hull via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> I keep wanting a “protected” access level, but I must also admit that there was something really elegant about Swift 2’s access scheme (and I think most of us feel like the word ‘fileprivate’ feels out of place).  I was thinking about how to mesh those two ideas, and I think I may have come up with a solution.
>> 
>> I propose we replace ‘fileprivate’ with a new ‘hidden’ access level.  Hidden would work exactly the same way as fileprivate does now, but adds the connotation that what is hidden can also be unhidden.  By adding ‘import hidden TypeName’ to another file, that file also gains access to all of the hidden items of that type (kind of like if it was in the same file).
>> 
>> #FileA
>>         import Foundation
>> 
>>         Struct A {
>>                 private var x:Int
>>                 hidden var y:Int  //This is just like fileprivate, but can also be shared with other files
>>         }
>> 
>>         extension A {
>>                 //y can be accessed here because they are in the same file
>>         }
>> 
>> 
>> #FileB
>>         import Foundation
>>         import hidden A  //This allows the entire file to see A’s hidden variables
>> 
>>         extension A {
>>                 //y can be accessed here because of the ‘import hidden’ declaration
>>         }
>> 
>> 
>> #FileC
>>         import Foundation
>> 
>>         extension A {
>>                 //y can NOT be seen or accessed here because it is hidden
>>         }
>> 
>> 
>> I think this is a fairly elegant solution to our protected dilemma, which also feels in sync with Swift 2’s file-based scheme.  The key features:
>>         • Extensions no longer need to be piled in the same file if it is getting too long
>>         • Subclasses can be in their own file, but still have access to the necessary parts of their superclass
>>         • It communicates the author’s intent that the items are not meant to be visible to its users, but that it is expected to be used for extension/subclassing
>>         • It requires an explicit statement ‘import hidden’ to access the hidden variables. Safe by default, with override.
>>         • It is not bound by module boundaries  (i.e. you could use it for subclassing classes from an imported module)
>>         • Roughly the same length as ‘private’ and ‘public’ so various declarations packed together are much easier to read (fileprivate breaks reading rhythm)
>> 
>> Worth a formal proposal?
>> 
>> Thanks,
>> Jon
>> 
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161016/11fe4aac/attachment.html>


More information about the swift-evolution mailing list