[swift-evolution] final + lazy + fileprivate modifiers

Matthew Johnson matthew at anandabits.com
Mon Feb 20 10:22:31 CST 2017


> On Feb 20, 2017, at 5:43 AM, Ross O'Brien via swift-evolution <swift-evolution at swift.org> wrote:
> 
> My two cents:
> 
> I like that Swift has a way of restricting access to some properties and functions to just the scope in which they're declared (Swift 3 private).
> 
> At the moment I tend to use the Swift idiom of declaring a type, and then declaring an extension each protocol conformance, and sometimes that means putting a property in the type rather than the extension and that requires fileprivate. That's not ideal, but if there were an idiomatically neater way to declare a private property in the extension and have Swift recognise that property as being part of the type but contextually linked only to that protocol conformance then that would be good.
> 
> 'Fileprivate' makes files equivalent to submodules, and this can lead to undesirably long files. Possibly the dependence on filesystem structure feels restrictive to some developers. It seems to me that in this four-tier structure (public, internal, fileprivate, private) that a significant number of developers think that's one, maybe two tiers too many (the system should be simplified), and for others that's one, maybe two tiers too few (fileprivate is doing too much, and a submodule tier would relieve that pressure).
> 
> I think a submodule access level has been suggested a lot of times, but I don't think anyone's formally proposed a possible syntax for it.
> 
> As Goffredo Marocchi said, I don't want to return to Objective C's dozen import statements at the top of a file, but how would we denote that a file is a member of a submodule? At the moment I think there are four ways - have I missed any?
> 
> 1) Submodules correspond directly to file directories (simple, but possibly binds a project too closely to an operating system, inflexible for systems like Git).
> 2) The submodule has an access control file which lists its member files (as modules do)
> 3) Files opt-in to membership of a submodule (e.g. 'memberOf Submodule' just below 'import Foundation')
> 4) Files 'friend' other files into their submodule, somehow.
> 
> Worth noting: the latter three of these suggestions don't implicitly prevent a file from being a member of more than one submodule.
> 
> I don't have my own answer to this question yet, but I think discussion of it will help decide whether submodules are a practical possibility. If they are, perhaps there's justification for fileprivate; if not, perhaps reverting to Swift 2's private is better.

Thanks for bringing up submodules again!

I think a well-designed, flexible submodule mechanism would be very nice to have.  There are many issues that should be addressed along the way.  Here are just a few:

* How do we define the files that make up a submodule? (as you point out)
* Are submodules hierarchical, allowing submodules to be nested inside of another submodule?
* How does a submodule specify visibility of a member that is not public but is available to the rest of the module (or perhaps just a parent submodule)?
* Can submodules have cross-dependencies?
* Should it be possible for users of a module to import only a submodule rather than the entire module?
* Should it be possible to compile submodules independently?
* How do submodules interact with whole module optimization?
* How do submodules address (or not address) namespacing?

The last question is somewhat important.  The design of submodules should not proceed without considering namespaces and whether or not submodules are intended to address this problem.  In some languages, code in another file can introduce new names into a namespace declared elsewhere (i.e. they are module-less).  Is this important?  Some people may want to have namespaces that are open to extension (which obviously submodules are not).  What are the use cases?  If we decide we don’t need namespaces that are open to extension then submodules may be a suitable mechanism for handling use cases sometimes addressed with namespaces.

One interesting thing to note is that if we do introduce submodules, especially if we introduce hierarchical submodules, then we have a pretty clean hierarchy of nested scopes.  We could even say that files are an anonymous submodule.  The picture looks like this:

module
     |
grandparent submodule
     |
parent submodule
     |
submodule
     |
anonymous file submodule
     |
outer lexical scope
     |
inner lexical scope

If we go in this direction and we want to allow submodules to expose a member upwards to a containing submodule, but not to the rest of the module as a whole, we will need some kind of way to refer to the name of the containing submodule.  One logical way to do that is to simply parameterize `private` as some have suggested:

private // current lexical scope
private(OuterType) // imagine this: struct OuterType { struct InnerType { private(OtherType) var x = 42 } }
private(file)
private(ParentModule)
private(GrandparentModule)

The argument would be required to be a containing scope.  This is pretty elegant and powerful.  Unfortunately, it doesn’t work well with the behavior people expect from `private` because Swift relies heavily on extensions.  People want `private` to be a good default that works well with the idiom of using extensions.

This is why I think we should not abandon scoped access, but instead give it a new name.  Scoped access does not need to be just about scopes *within* a file.  This mechanism can be used for any scopes.  Imagine reverting `private` to have the behavior people expect and using `scoped` for the more general mechanism:

scoped // current lexical scope
scoped(OuterType) // imagine this: struct OuterType { struct InnerType { private(OtherType) var x = 42 } }
scoped(file) // private is an alias for this
scoped(ParentSubmodule)
scoped(GrandparentSubmodule)
scoped(module) // internal is an alias for this

This allows us to have a *single* inside the module access control mechanism which is powerful enough to handle submodules well that just happens to have a couple of syntactic sugar aliases for the most common use cases.  We could even ditch `internal` since it is the default and thus rarely stated explicitly.  We would have another available for those who do wish to state it explicitly.

If we want to have an elegant but powerful system of access control I really think this is the way to go.  If we don’t do this we could easily end up with a clunkier and less powerful mechanism that only works with submodules.


> 
> Thanks,
> Ross
>   
> 
> On Mon, Feb 20, 2017 at 10:41 AM, Dimitri Racordon via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> I really don’t like the idea of an attritional access control file.
> 
> Not everybody is using an IDE that automagically creates the structures and files for your favourite language, and I would hate to see Swift go the Java way where it’s impossible to build something seriously without Eclipse or Netbeans. Xcode is great, but I think we should not design Swift thinking it’s the norm to use it.
> 
> I’d favourite any kind of syntax that is baked in the same file as the code.
> 
> Thanks,
> Dimitri
> 
> _______________________________________________
> 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
> https://lists.swift.org/mailman/listinfo/swift-evolution

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


More information about the swift-evolution mailing list