I believe `submodule` in my proposal introduces a segmented-scope-like thing, 
akin to how the various files included in a module are combined as one 
collection of declarations. And you have the ability to import things from that 
scope with `import submodule`, like with modules. I think the name submodule 
fits as some sort of "lesser module", but if someone has a better name for this 
I'll listen.

We could add a submodule-private access level, but I'm not sure it is worth its 
weight. There's plenty of possibilities, but I know I wouldn't be too happy 
with more access levels than what we have now. For each access level you add 
the more fine-grained they become and the more difficult it is to decide which 
one is the right one. Hence why I'm keeping the idea of adding access levels as 
a separate concern worth of its own debate. This proposal is solely focused on 
providing intra-module boundaries, and I think those boundaries can work 
without introducing a new access level.

> On 6 mars 2017, at 2:39, Robert Widmann <[email protected]> wrote:
> 
> If this were a problem that could be fixed by tooling, you'd be submitting 
> this proposal against the package manager.
> 
> It's important to note I'm approaching this problem from the principles that 
> underpin modular programming practices in other languages, it is not merely 
> "philosophical".  A proposal that claims to support submodules should come 
> with some consistent support for modularity or rebrand to more honestly 
> reflect its contents.  As it stands, this proposal effectively introduces 
> another level of access (while paradoxically claiming to avoid access control 
> discussions), and suggests more access control to support its contents later. 
>  Maybe discussion should start there instead of with "submodules".
> 
> ~Robert Widmann
> 
> 2017/03/06 0:39、Michel Fortin <[email protected] 
> <mailto:[email protected]>> のメッセージ:
> 
>> 
>>> On 5 mars 2017, at 22:28, Robert Widmann <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>> 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] <mailto:[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 
>>>> <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.
>> 
>> Well maybe that's just a problem specific to me, but that's the goal I'd 
>> like submodule to solve. If I have four intermingled classes, I'd like to 
>> restrict the interminglement to those four classes. My current options for 
>> that are a) to create a separate module, b) to put them all in the same file 
>> with `fileprivate`, or c) tolerate that the rest of the module will see 
>> everything all the time with `internal`. I'm choosing (c) at the present 
>> time because (a) is too much work and (b) is more messy.
>> 
>> You are absolutely right in your observation that it's some sort of 
>> "friend". But then the same observation could apply to the `internal` 
>> members in a module or the `fileprivate` members in a file. The delimiting 
>> boundaries are different, but the concept is the same. What I want is the 
>> ability to better define those boundaries.
>> 
>>>> 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.
>> 
>> Good catch. If you have a module called "submodule" and you import it, 
>> that'll break.
>> 
>>>> 
>>>> 
>>>> ## 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.
>> 
>> I'll say that this definition of `public` is quite handy as long as the 
>> submodules are implementing public-facing functionality. I agree it is less 
>> useful when the submodule is the backend of some other component in the 
>> module. You are thus right in that it naturally organizes things 
>> horizontally.
>> 
>> So if you want to use submodules to organize self-contained dependencies, my 
>> approach does not work very well. In particular, there is no containment of 
>> anything `internal` when you `import submodule Foo`. That would be improved 
>> if we had submodule-private as discussed in Future Directions... with the 
>> caveat that it's still easy to access any submodule's internals by declaring 
>> any file to be part of the submodule. And also you want a strict hierarchy, 
>> which this proposal completely eschew.
>> 
>> We obviously have a different philosophy on how strict and hierarchic the 
>> submodules should be. I say the strictness should stay at the module 
>> boundaries. If you want enforced self-contained modules, change those 
>> submodules into full modules. If you want to ship the whole thing as a 
>> single library, then we need to adapt the tooling so you can merge privately 
>> those modules inside of the main library. (Ideally this would be done with a 
>> single compiler invocation, allowing full cross-module optimization.) This 
>> is a tooling and packaging problem that does not need to leak into a 
>> plethora of new language concepts. At least, this is how I see it.
>> 
>> 
>>>> 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 <https://michelf.ca/>
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> [email protected] <mailto:[email protected]>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> -- 
>> Michel Fortin
>> https://michelf.ca <https://michelf.ca/>

-- 
Michel Fortin
https://michelf.ca

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to