This proposal strikes me not as a submodules proposal, but as a sneaky way to add 'friend' to Swift. It is conceptually simpler because it doesn't address modularity at all!
~Robert Widmann 2017/03/05 17:16、Michel Fortin via swift-evolution <[email protected]> のメッセージ: > Sorry for introducing yet another submodule proposal out of the blue. > > I'm a bit surprised at how far-reaching the various submodule proposals > floated on this list have been. Directories, access controls, @exported > imports... For comparison's sake here's one that is *really* simple and short > I wrote today. Best of all: it requires no new access modifier. > > I also expect everyone to scream at it because it does not include all the > desirable features of a submodule system. At the very least I'll have > redefined the meaning of lightweight in that discussion. Good reading. > > Also available here: > https://gist.github.com/michelf/779b1bc26a778051b6231b5639665e18 > > > ## Motivation > > The goal of this proposal is to provide lightweight intra-module boundaries > so you can avoid exposing every `internal` declaration within a big module to > all other files in the module. > I don't think this was a stated goal of anybody because that’s not the primary issue submodules are trying to solve. Projects with a large amount of internal declarations may be clamoring to be put into multiple (sub)modules, but because the style most have converged on doesn’t usually involve a large amount of free functions, your internal declarations are usually types and as such have members that are already encapsulated. > Not a goal: addressing the file-level access fileprivate/private or > scoped/protected debates. This is left to other proposals. > > > ## Summary > > This proposal adds the declarations `submodule` and `import submodule`. It > also limits the visibility of `internal` to files with a matching `submodule` > or `import submodule` declaration. > > Submodules are never exposed outside of the module. They only change the > visibility of internal declarations, so there is no point in exposing them > publicly. > > Submodules are not bound to directories, nor are they necessarily > hierarchical. > > This change is purely additive and introduces no source compatibility issue. This particular set of changes is, but the `import submodule` change is source-breaking in that we already have syntax for this. > > > ## Details > > A `submodule <name>` declaration at the beginning of a file contains an > identifier with the submodule name: > > submodule Foo > > internal func foo() {} > public func pub() {} > > `internal` declarations within that file are only visible to other files > sharing the same submodule name. The submodule only protects `internal` > declarations: `public` declarations in the file are visible everywhere (in > other submodules and in other modules). > This definition of public is anti-modular. It seems as though this style of submodule will encourage people to split their definitions across multiple files (good) then provide a pile of submodule-membership definitions to intertwine them with each other as they need access to each other’s symbols (bad). A module is a self-contained entity by definition. Dependencies should generally be on further child (sub)modules - vertically, not horizontally. > A file can be part of more than one submodule: > > submodule Foo > submodule Bar > > internal func achoo() { > foo() // available in Foo (from other file) > } > > This makes the internal `achoo` function visible within both the `Foo` and > `Bar` submodules. Also note that it makes internal members of both submodules > `Foo` and `Bar` visible within the file. > > A file can access internal declarations of a submodule without having to > expose its own internal functions to the submodule with `import submodule`: > > submodule Test > import submodule Foo > > internal func test() { > foo() // internal, imported from Foo > achoo() // internal, imported from Foo > pub() // public, so always visible > } > > Finally, when a file has no submodule declaration, its internal declarations > are visible everywhere in the module and all its submodules: > > --- Hello.swift --- > // no submodule declaration > internal func hello() {} > > --- World.swift --- > submodule World > internal func test() { > hello() // visible! > } > > > ## Nitpicky Details (Conflicting Declarations) > > Declaring `internal` things that share the same name in two separate > submodules is not a conflict: > > --- Foo1.swift --- > submodule Foo1 > class Foo {} // added to Foo1 > > --- Foo2.swift --- > submodule Foo2 > submodule Foo3 > class Foo {} // added to Foo2 and Foo3 > > (Note: It would be a conflict if one of them was `public`, because `public` > declarations are always visible everywhere inside (and outside of) the > module.) > > Attempting to use both from another submodule will create ambiguity errors. > You can disambiguate using the submodule name as a prefix: > > --- Main.swift --- > import submodule Foo1 > import submodule Foo2 > import submodule Foo3 > let f0 = Foo() // ambiguity error > let f1 = Foo1.Foo() // good > let f2 = Foo2.Foo() // good > let f3 = Foo3.Foo() // good > > Best to avoid this for your own sanity however. > > > ## Alternatives Considered > > ### Conflicting Declarations > > Instead of allowing conflicting symbols in different submodules, we could > continue to disallow conflicting `internal` declarations even when they > belong to different submodules. This would make the design simpler, as it is > closer to how `internal` currently works and prevent ambiguity errors from > arising when importing multiple submodules. The behavior would be a little > surprising however. > > We could also simplify by removing the ability to use the submodule name as a > prefix to disambiguate. This has the advantage that if you put a type inside > of a submodule with the same name, no conflict can arise between the name of > the type and the name of the submodule. Disambiguation would have to be done > by renaming one of the conflicting declarations. Since this ambiguity can > only affect `internal` declarations (submodules only group internal > declarations), requiring a rename will never break any public API. But > forcing a rename is not a very elegant solution. > > ### `import` syntax > > Renaming `import submodule` to `import internal`. Having "internal" in the > name could make it clearer that we are giving access to internal declarations > of the submodule. But it also make the import less relatable to the > `submodule` declaration in other files. > > > ## Future Directions > > ### Submodule-Private > > While a submodule-private access modifier could have been added to this > proposal, the belief is that this proposal can live without it, and not > having this greatly reduce the little details to explore and thus simplifies > the design. > > In many cases you can work around this by putting "private" stuff in a > separate submodule (somewhat similar to private headers in C land). For > instance: > > --- Stuff.swift --- > submodule Stuff > submdoule StuffImpl > > func pack() { packImpl() } > > --- StuffImpl.swift --- > submodule StuffImpl > > func packImpl() { ... } > > This will not work for stored properties however. A future proposal could > suggest allowing stored properties in extensions to help with this. > > And a future proposal could also add submodule-private to limit visibility of > some declarations to only those files that are part of a specific module. > > > -- > Michel Fortin > https://michelf.ca > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
