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.

Thanks,
Jon


* 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to