<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">How do you define 'outsider'? What is the threat model you want to be protected from?</div><div class=""><br class=""></div><div class="">I believe it is someone on your team or a contributor not following the established policies for the project, in particular the policies related to encapsulation. Those policies are necessarily going to vary from project to project and from team to team. It is to accommodate these varied cases and policies that the other submodule proposals have plenty of different ways submodules can be organized and used, and also why they are complex.</div><div class=""><br class=""></div><div class="">Perhaps instead of baking those policies as language features the team should use a pre-commit linter tool enforcing them. This proposal should make it simpler to write such a linter.</div><div class=""><br class=""></div><div><blockquote type="cite" class=""><div class="">On 6 mars 2017, at 2:47, Rien &lt;<a href="mailto:Rien@balancingrock.nl" class="">Rien@balancingrock.nl</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">+1 for the simplicity. I like that.<br class=""><br class="">However I dislike the non-hierachical approach: It should not be possible for an â€˜outsider’ to declare itself part of a submodule if the original developer of that submodule did not want that. I understand that the submodules are not exported through the module (though wait for the requests to do so if this were ever implemented) but if large teams want to use submodules to structure their code base, some level of hierarchy is imo necessary.<br class=""><br class="">Regards,<br class="">Rien<br class=""><br class="">Site: <a href="http://balancingrock.nl" class="">http://balancingrock.nl</a><br class="">Blog: <a href="http://swiftrien.blogspot.com" class="">http://swiftrien.blogspot.com</a><br class="">Github: <a href="http://github.com/Balancingrock" class="">http://github.com/Balancingrock</a><br class="">Project: <a href="http://swiftfire.nl" class="">http://swiftfire.nl</a><br class=""><br class=""><br class=""><br class=""><br class=""><br class=""><blockquote type="cite" class="">On 05 Mar 2017, at 23:16, Michel Fortin via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""><br class="">Sorry for introducing yet another submodule proposal out of the blue.<br class=""><br class="">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. <br class=""><br class="">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.<br class=""><br class="">Also available here: <br class=""><a href="https://gist.github.com/michelf/779b1bc26a778051b6231b5639665e18" class="">https://gist.github.com/michelf/779b1bc26a778051b6231b5639665e18</a><br class=""><br class=""><br class="">## Motivation<br class=""><br class="">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.<br class=""><br class="">Not a goal: addressing the file-level access fileprivate/private or scoped/protected debates. This is left to other proposals.<br class=""><br class=""><br class="">## Summary<br class=""><br class="">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.<br class=""><br class="">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.<br class=""><br class="">Submodules are not bound to directories, nor are they necessarily hierarchical.<br class=""><br class="">This change is purely additive and introduces no source compatibility issue.<br class=""><br class=""><br class="">## Details<br class=""><br class="">A `submodule &lt;name&gt;` declaration at the beginning of a file contains an identifier with the submodule name:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Foo<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>internal func foo() {}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>public func pub() {}<br class=""><br class="">`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).<br class=""><br class="">A file can be part of more than one submodule:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Foo<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Bar<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>internal func achoo() {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>foo() // available in Foo (from other file)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">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.<br class=""><br class="">A file can access internal declarations of a submodule without having to expose its own internal functions to the submodule with `import submodule`:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Test<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>import submodule Foo<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>internal func test() {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>foo() // internal, imported from Foo<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>achoo() // internal, imported from Foo<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>pub() // public, so always visible<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">Finally, when a file has no submodule declaration, its internal declarations are visible everywhere in the module and all its submodules:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- Hello.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// no submodule declaration<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>internal func hello() {}<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- World.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule World<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>internal func test() {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>hello() // visible!<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class=""><br class="">## Nitpicky Details (Conflicting Declarations)<br class=""><br class="">Declaring `internal` things that share the same name in two separate submodules is not a conflict:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- Foo1.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Foo1<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class Foo {} // added to Foo1<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- Foo2.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Foo2<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Foo3<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class Foo {} // added to Foo2 and Foo3<br class=""><br class="">(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.)<br class=""><br class="">Attempting to use both from another submodule will create ambiguity errors. You can disambiguate using the submodule name as a prefix:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- Main.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>import submodule Foo1<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>import submodule Foo2<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>import submodule Foo3<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let f0 = Foo() // ambiguity error<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let f1 = Foo1.Foo() // good<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let f2 = Foo2.Foo() // good<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let f3 = Foo3.Foo() // good<br class=""><br class="">Best to avoid this for your own sanity however.<br class=""><br class=""><br class="">## Alternatives Considered<br class=""><br class="">### Conflicting Declarations<br class=""><br class="">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.<br class=""><br class="">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.<br class=""><br class="">### `import` syntax<br class=""><br class="">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.<br class=""><br class=""><br class="">## Future Directions<br class=""><br class="">### Submodule-Private<br class=""><br class="">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.<br class=""><br class="">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:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- Stuff.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule Stuff<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submdoule StuffImpl<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>func pack() { packImpl() }<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>--- StuffImpl.swift ---<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>submodule StuffImpl<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>func packImpl() { ... }<br class=""><br class="">This will not work for stored properties however. A future proposal could suggest allowing stored properties in extensions to help with this.<br class=""><br class="">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. <br class=""><br class=""><br class="">-- <br class="">Michel Fortin<br class="">https://michelf.ca<br class=""><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class="">swift-evolution@swift.org<br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></blockquote><br class=""></div></div></blockquote></div><br class=""><div class="">
<div style="color: rgb(0, 0, 0); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="color: rgb(0, 0, 0); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="color: rgb(0, 0, 0); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="color: rgb(0, 0, 0); font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><span class="Apple-style-span" style="border-collapse: separate; font-variant-ligatures: normal; font-variant-east-asian: normal; font-variant-position: normal; line-height: normal; border-spacing: 0px;"><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; border-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px;"><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: Helvetica; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; border-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-stroke-width: 0px;"><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">--&nbsp;<br class="">Michel Fortin</div><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><span style="text-align: -webkit-auto;" class=""><a href="https://michelf.ca" class="">https://michelf.ca</a></span></div></span></div></span></div></span></div></div></div></div>
</div>
<br class=""></body></html>