> On 03 Mar 2017, at 18:54, Karim Nassar <[email protected]> wrote: > > Thanks for your feedback. > > These are very fair concerns and point taken. There are other mechanisms we > could use to achieve similar ends, I’ll work on some additional ideas. > > How do you view the overall goals/use cases?
I’m rather neutral on the need for modules/submodules. The points you mentioned don’t sound bad, but then again they are kind-a subjective. Regards, Rien. > > —Karim > >> On Mar 3, 2017, at 10:58 AM, Rien <[email protected]> wrote: >> >> I don’t like the file location based approach taken, see inline. >> >> Rien. >> >>> On 03 Mar 2017, at 16:24, Karim Nassar via swift-evolution >>> <[email protected]> wrote: >>> >>> >>> I’ve read through the last couple of Swift (sub)Module proposals put >>> forward, and since my particular use-cases for a sub-module solution seemed >>> to be under-served by them, I’ve decided to write up my thoughts on the >>> matter to prompt discussion. >>> >>> Perhaps my use-cases are outliers, and my approach will be deemed naive by >>> the community… I’m happy to learn better ways of doing things in Swift, and >>> welcome any thoughts, criticism, or illumination related to these ideas. >>> >>> I’m including the write-up below, but it’s also available as a gist: >>> https://gist.github.com/anonymous/9806f4274f1e13860670d6e059be5dce >>> >>> — >>> >>> # Sub-modules >>> >>> A sub-module solution in Swift should have the following properties: >>> >>> * Extremely light-weight >>> * Low API surface area >>> * Adopt progressive disclosure >>> * Integrate with Access Control features to enable a level of encapsulation >>> & hiding between the Module and File level >>> * Be permeable when desired >>> >>> ## Discussion >>> >>> As we get deeper into building real applications & frameworks with Swift, >>> we begin to realize that having a way to express relationships between >>> types is desireable. Currently, Swift only allows us to express these >>> relationships at two levels, the Module and the File. >>> >>> The Module boundary is acceptable for small, focused frameworks, while the >>> File boundary is acceptable for small, focused Types, but both levels can >>> be unweildy when dealing with certain cases where a cluster of internally >>> related types needs to know about each other but may only want to publish a >>> narrow set of APIs to the surrounding code, or in large complex >>> applications which are necessarily structured as a single Module. In these >>> cases, we wind up with large monolithic Modules or (even worse) large >>> monolithic Files. >>> >>> I have seen this proliferation of Huge Sprawling Files (HSFs) in my own >>> code, and seek a way to combat this rising tide. >>> >>> ## Goals >>> >>> It is a goal of this proposal to: >>> >>> * Suggest a mechanism for organizing code between the Module and File >>> levels that is as lightweight and low-friction as possible >>> * Provide mechanisms for authors to create both "hard" and "soft" API >>> boundaries between the Module and File levels of their code >>> >>> ## Anti-Goals >>> >>> It is not a goal of this proposal to: >>> >>> * Move Swift away from filesystem-based organization >>> * Significantly alter the current Access Control philosophy of Swift >>> >>> ## Proposal Notes >>> >>> Please take the following proposal wholely as a Straw-Man... I would be >>> equally satisfied with any solution which meets the critera described at >>> the top of this document. >>> >>> Unless specified otherwise, all spellings proposed below are to be >>> considered straw-men, and merely illustrative of the concepts. >>> >>> ## Proposed Solution >>> >>> Two things are clear to me after using Swift and following the Swift >>> Evolution list since their respective publications: >>> >>> 1. Swift has a preference for file-based organization >>> 2. Vocal Swift Users dislike `fileprivate` and want to revert to >>> Swift2-style `private` >>> >>> Because of #1, this proposal does not seek to change Swift's inherent >>> file-system organization, and instead will expand on it. >>> >>> Since I personally fall into the camp described by #2, and most of the >>> community response to this has been "Lets wait to deal with that until >>> sub-modules", I'm making this proposal assuming that solving that quagmire >>> is in-scope for this propsoal. >>> >>> ### Changes to Access Control Modifiers >>> >>> As part of this proposal, I suggest the following changes to Swift 3's >>> Access Control modifiers: >>> >>> * Revert `private` to Swift 2's meaning: "hidden outside the file" >>> * Remove `fileprivate` as redundant >>> >>> This is potentially a source-breaking change. However, it is interesting to >>> note that this change is **not** required for the following proposal to >>> function. >>> >>> Changes that *are* necessary are: >>> >>> * Change the spelling of `internal` to `module` (making `module` the new >>> default) >>> * Introduce a new modifier `internal` to mean "Internal to the current >>> sub-module and its child-sub-modules" >>> >>> These changes are *not* source-breaking because the new `internal` modifier >>> acts exactly as the old `internal` modifier unless it is used within a >>> sub-module. The specific spelling of this new `internal` modifier is >>> necessary to maintain backwards source compatibility. >>> >>> The new `module` modifier allows authors to make APIs permeable between >>> sub-modules while still hidden outside the owning Module if desired. >>> >>> All other Access Control modifiers behave the same as they currently do >>> irrespective of sub-module boundaries, so: >>> >>> * `public` => Visible outside the Module >>> * `open` => Sub-classable outside the Module >>> >>> ### Making a Sub-module >>> >>> To create a sub-module within a Module (or sub-module) is simple: The >>> author creates a directory, and places a "sub-module declaration file" >>> within the directory: >> >> I don’t like “behind the scenes” magic. Any file should stand on its own. >> i.e. I want to see in the file itself what it can do, and what it uses from >> other files. Everything should be defined in the file itself. >> >> Its place in the file system should be completely irrelevant. >> >>> >>> ``` >>> // __submodule.swift >>> // MyModule >>> >>> submodule SubA >>> >>> ``` >>> >>> Then any files within that directory are part of the sub-module: >>> >>> ``` >>> // Foo.swift >>> // MyModule.SubA >>> >>> struct Foo { >>> private var mine: Bool >>> internal var sub: Bool >>> module var mod: Bool >>> } >>> >>> public struct Bar { >>> module var mod: Bool >>> public var pub: Bool >>> } >>> >>> ``` >>> >>> This creates a sub-module called "SubA" within the module "MyModule". All >>> files within the directory in which this file appears are understood to be >>> contained by this sub-module. >>> >>> If in the future we choose to add additional complexity (versioning, >>> #availability, etc) to the sub-module syntax, the sub-module declaration >>> gives a natural home for this configuration. >>> >>> It's important to note some benefits of this approach: >>> >>> * Using the "special file" means that not all Directories are automatically >>> submodules >>> * Any given source file may only be a member of 1 submodule at a time >>> * Use of filesystem structure to denote sub-modules plays nicely with >>> source control >>> * The sub-module structure is instantly clear whether using an IDE (which >>> can be taught to parse the `__submodule.swift` files to decorate the UI), >>> or simple text-editor (assuming a convention of naming the Directory the >>> same as the sub-module, which is a linter problem) >>> >>> ### Using Sub-modules >>> >>> Referencing a sub-module should be natural and clear at this point: >>> >>> #### From Within the Parent Module/Sub-module >>> >>> Sub-modules are simply code-organization & namespacing tools within >>> modules. As such, when referenced from within their parent Module, there is >>> no need for `import`s >> >> >> When I read code, the location or source of each item should be clearly >> traceable. Either by lexical structure or by (path) file name. If I can >> access the contents of another file based on the location of that other file >> in the file system… nope that will generate problems due to “missing file” >> etc. >> >> Besides I think this could easily become a nightmare for autocomplete in >> IDEs ? >> >>> >>> ``` >>> // in MyModule >>> >>> let foo = SubA.Foo() >>> foo.mine = true // Compiler error because it's private >>> foo.sub = true // Compiler error because it's internal to the sub-module >>> foo.mod = true // OK >>> >>> ``` >>> >>> #### From Outside the Parent Module/Sub-module >>> >>> When referenced from outside their parent Module, one imports the whole >>> module in the standard way: >>> >>> ``` >>> import MyModule >>> >>> let foo = SubA.Foo() // Compiler error because it's internal to the Module >>> >>> let bar = SubA.Bar() // OK >>> bar.mod = true // Compiler error because it's internal to the Module >>> bar.pub = true // OK >>> >>> ``` >>> >>> ## What this Proposal Deliberately Omits >>> >>> This proposal deliberately omits several concepts which may be integral to >>> various use-cases for sub-modules, primarily because they can be treated as >>> purely additive concepts and I don't wish to weigh down the consideration >>> of the overall approach with a larger API surface area that might be >>> debated separately. I.e: Keep it as small as possible for now, then if it's >>> any good, iterate on the design. >>> >>> ### Inter-Sub-Module Access Control >>> >>> One might ask given a sub-module structure like: >>> >>> ``` >>> MyModule >>> | >>> +--- SubA >>> | >>> +--- SubB >>> >>> ``` >>> >>> "How can SubB hide properties from MyModule without hiding them from SubA?" >>> >>> This is a valid question, and not answered by this proposal for two reasons: >>> >>> * This trivial case could be solved by simply adding a new modifier >>> `submodule` if we so desired, but: >>> * In the absence of any direct response, the status-quo provides a >>> work-around: Omit the sub-sub-module structure and use the file-access >>> constraints of `private` >>> * This overall problem probably should be solved by addressing larger >>> questions in the Access Control scheme of Swift, irrespective of the >>> sub-module mechanism >>> >>> ### Expressiveness of Sub-module Imports >>> >>> One might ask: "Why can't I import only a specific sub-module or alias a >>> sub-module?" >>> >>> I have ignored this aspect of submodules because the question of `import` >>> expressiveness is a separate issue in my mind. The fact that we cannot say: >>> >>> ``` >>> import MyModule as Foo >>> ``` >>> >>> Has no relationship to the lack of sub-modules in Swift. >>> >>> If the community deems it an important enough use-case to warrant altering >>> import behavior, so be it, but that can be treated as purely additive to >>> this proposal. >>> >>> But it should be understood that this approach to sub-modules is not >>> designed to provide an expressive "exports" capability. It is primarily >>> interested in organizing code *within* a Module >>> >>> >>> >>> >>> _______________________________________________ >>> 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
