> On Mar 6, 2017, at 10:22 AM, Michel Fortin <[email protected]> wrote: > > 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. >
My beef is not with that, my beef is with the idea that `public` should leak APIs across module boundaries by default and that a module is merely a way to section off internal declarations. Matthew has given these a name in his scoped access proposal, and Swift today gives the kind of scope you’re trying to define a name - it’s called fileprivate. > 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] >> <mailto:[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 <https://michelf.ca/>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
