[swift-evolution] Simplifying Access Using 'Hidden'

Adrian Zubarev adrian.zubarev at devandartist.com
Mon Feb 13 08:15:38 CST 2017


I won’t even try to be constructive on this one. It simply makes me tired of all this access modifier mess. open, closed, public, internal, now hidden, fileprivate, directoryprivate, moduleprivate, private, I might even forget some of the proposed access modifiers.

Instead of adding new stuff that explodes the complexity we should put our energy and fix existing issues, like the inconsistent open for example.

Adrian Zubarev
Sent with Airmail

Am 13. Februar 2017 um 15:06:45, Jonathan Hull via swift-evolution (swift-evolution at swift.org) schrieb:

I wanted to propose a second simplification to the access system which would have several benefits:

• It would remove ‘fileprivate’
• It would allow extensions to be in their own files
• It would serve the needs of ‘protected’ without the complications involved in that
• It would serve some of the needs of submodules (but also work together with them really nicely when we have both)
• It would allow protocols to have something similar to private methods, which are not exposed to callers of the protocol, but still required for conformance

My proposal is to add a ‘hidden’ access modifier which would act as a separate AXIS as opposed to a new level. Thus, it could be combined with any of the other modifiers (e.g. 'public hidden var’). The ‘fileprivate’ level would be removed.

Anything which is marked hidden is not visible outside of the file it is defined in. That visibility can be explicitly restored on a per file basis (but only within the defined access level). Take a look at the following:

//File: MyStruct.swift

struct MyStruct {
var a:Int
hidden var b:Int

extension MyStruct {
var biggest:Int {return max(a,b)} //We can still see ‘b’ because it is only hidden outside the file

Here we see that ‘b’ behaves very similarly to the way fileprivate works now. ‘b’ is technically still internal, but it is hidden and thus can’t be seen outside the file. The difference is that instead of being forced to add all of our extensions in the same file, we can organize them however we prefer.

//In another file
import hidden MyStruct //This exposes all ‘hidden’ items from the file MyStruct to this file

extension MyStruct {
var c:Int {return a + b} //We can see ‘b’ because of the ‘import hidden’ statement

Notice how the intent has been shown here. ‘b’ is marked ‘internal’, which means we know that no one can see ‘b’ outside of the module. In addition ‘b’ is marked ‘hidden’, which means that the author is saying that this should only really be accessed from extensions, subclasses, or types which would be called friends in other languages. The actual guarantee is still ‘internal’, but a caller does have to explicitly request the access by typing ‘import hidden’, which will stop accidental/casual misuse. (If/when we get submodules, then we can make tighter guarantees)

This also allows a class to “hide the ejection seat levers” from its callers, while still allowing access for subclasses and extensions (i.e. it does most of what ‘protected’ would do, but with swift’s simpler file-based access model)

The same is true of protocols. There are often methods I have to include on protocols which are needed by the default implementations, but NEVER meant to be called directly by callers of the protocol. If vars/methods of a protocol are marked hidden, then they would be hidden from callers of the protocol outside the file. They are still required for conformance, however*.  

protocol MyProtocol {
var a:Int
hidden var b:Int //Conformers still need to provide this, but callers can’t see it

extension MyProtocol {
func doSomethingBasedOnB() {
//The extension can see b in the same file, but callers in other files won’t have access to ‘b’ directly

I think all of this works really well with Swift’s goal of progressive disclosure. Users of protocols/classes/etc… are not exposed to the internals necessary for extension (e.g. it won’t come up in autocomplete) until they actually need to conform/subclass/extend. Users won’t even need to learn about ‘hidden' until they are trying to extend a type which uses it (in a way which requires access to those internals), or they are writing their own framework. It has a simple and consistent meaning based on Swift’s original file-based access, but allows power/flexibility (without too much bookkeeping/boilerplate) where needed.


* One detail needed to make things useful for protocols, is that both hidden and non-hidden vars/methods on the conforming type should count towards conformance of hidden vars/methods on the protocol. Basically, marking something as ‘hidden’ on a protocol means it isn’t seen by the caller (only conformers/extensions). It makes sense to allow the conformer not to expose that either, unless desired. It would technically work fine without this allowance, but it ‘feels right’ and people would ask for it quickly...

swift-evolution mailing list
swift-evolution at swift.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170213/c6731c53/attachment.html>

More information about the swift-evolution mailing list