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]> のメッセージ: > >> On 5 mars 2017, at 22:28, Robert Widmann <[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]> のメッセージ: >> >>> 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. > > 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 >>> >>> _______________________________________________ >>> swift-evolution mailing list >>> [email protected] >>> https://lists.swift.org/mailman/listinfo/swift-evolution > > -- > Michel Fortin > https://michelf.ca >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
