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

Reply via email to