That’s an interesting proposal. Here are the issues I see:

- I don’t like nesting everything within a large “module” block. I would like a 
top-level “@module Foo” declaration for the entire file. Also, since any 
sub-modules would be nested within the implicit top-level module, I’d prefer 
the keyword “submodule”. Otherwise, what happens if I write:

module Foundation {
// Is this a module-extension? Can I insert new types or top-level functions in 
to Foundation?
// Am I creating a new top-level module named “Foundation”?
}

Looking at this thread:

- There are certainly issues with access control of submodules. OTOH, I don’t 
really like the idea that one incorrect import in one file can expose an entire 
submodule as part of your module’s API.

More generally:

- Access control of submodules seems like something which could benefit from a 
single source of truth.
- Can submodules have independent versions? Where would we declare them?

I wonder if we should have something like “module manifest” for all of a 
library’s public submodules, similar to SwiftPM's package manifest. So, 
strawman syntax…

Module(Foo, version: 1.2.2, description: “A library for foo-ing around with”, 
submodules: [
    Module(Foo.Maths, version: 1.5.0, description: “A maths library supporting 
Foo”),
    Module(Foo.Formatters, version: 1.5.0, description: “Formatters for Foo 
types”)
])

So, in this case we had a module Foo, then we updated FooMaths with some new 
APIs (say some new operations/types were added, and we updated FooFormatters 
accordingly). However, the API of Foo itself hasn’t changed; it’s the same as 
it was ages ago. Would that be possible?

> On 21 Feb 2017, at 02:47, Robert Widmann via swift-evolution 
> <[email protected]> wrote:
> 
> You’ll be delighted to know, then, that I’ve been thinking about this for a 
> few weeks now and have a draft proposal that will be submitted for discussion 
> shortly.  I believe this can be an additive feature and still preserve all 
> the goodness you would expect of a real module system.
> 
> ~Robert Widmann
> 
>> On Feb 20, 2017, at 8:36 PM, Brent Royal-Gordon via swift-evolution 
>> <[email protected]> wrote:
>> 
>> Okay, lots of people want to have some kind of submodule feature, so I'd 
>> like to sketch one out so we can hopefully agree on what submodules might 
>> look like.
>> 
>> ***
>> 
>> Any group of Swift files can be grouped together to form a submodule. 
>> Submodules belong within a particular module, and have a dotted name: If 
>> `ModKit` is a module, it might have a submodule called `ModKit.Foo`. 
>> Submodules can be nested within one another: `ModKit.Foo.Bar` is a submodule 
>> of `ModKit.Foo`, which is a submodule of `ModKit`.
>> 
>> No new access levels are necessary. `internal` APIs are only visible within 
>> the submodule they're declared in; a module cannot see its submodules' 
>> `internal` APIs, and a submodule cannot see its parent module's `internal` 
>> APIs. If a submodule wants to expose some of its APIs to its parent or 
>> sibling modules, it must mark them as `public` or `open`. Then they can 
>> import the submodule to see its APIs:
>> 
>>      import ModKit.Foo
>> 
>> By default, outside modules cannot import a submodule. But an import in the 
>> parent module can be decorated by an access control keyword to allow that:
>> 
>>      /// Any module outside ModKit can import ModKit.Foo and access its 
>> `public` and `open` APIs.
>>      open import ModKit.Foo
>> 
>>      /// Any module outside ModKit can import ModKit.Foo and access its 
>> `public` and `open` APIs, 
>>      /// except `open` APIs are treated as `public`.
>>      public import ModKit.Foo
>> 
>> Imports may also be decorated by the `@exported` attribute, which exposes 
>> the submodule's APIs as though they were parent module APIs:
>> 
>>      @exported open import ModKit.Foo
>> 
>>      @exported public import ModKit.Foo
>> 
>> (This is sort of half-implemented already in a buggy `@_exported` attribute.)
>> 
>> Finally, the standard syntax for importing individual symbols can be used to 
>> cherry-pick types to treat differently:
>> 
>>      // Most ModKit.Foo APIs are not importable...
>>      import ModKit.Foo
>> 
>>      // ...but SomeEnum can be imported as public...
>>      public import enum ModKit.Foo.SomeEnum
>> 
>>      // ...SomeClass can be imported as open...
>>      open import class ModKit.Foo.SomeClass
>> 
>>      // And ImportantStruct will import whenever you import ModKit.
>>      @exported public import struct ModKit.Foo.ImportantStruct
>> 
>> (This syntax should be enhanced to allow cherry-picked importing of global 
>> functions, constants, and variables.)
>> 
>> If there are several different `import`s covering the same submodule or 
>> submodule symbol, the most permissive one wins.
>> 
>> (In large projects, `public`, `open`, and `@exported` imports will most 
>> likely all be put in a single Policy.swift file or something, but this is 
>> not enforced by the language.)
>> 
>> A submodule may not import any direct parent module (parent, grandparent, 
>> etc.), but may import any other submodule in the same module. This list 
>> shows permitted imports for a project with four modules/submodules:
>> 
>>      ModKit
>>              - ModKit.Foo
>>              - ModKit.Foo.Bar
>>              - ModKit.Quux
>>      ModKit.Foo
>>              - ModKit.Foo.Bar
>>              - ModKit.Quux
>>      ModKit.Foo.Bar
>>              - ModKit.Quux
>>      ModKit.Quux
>>              - ModKit.Foo
>>              - ModKit.Foo.Bar
>> 
>> However, submodules may not form circular dependencies through imports—if 
>> `ModKit.Quux` imports `ModKit.Foo`, then `ModKit.Foo` cannot import 
>> `ModKit.Quux`. The `#if canImport()` feature cannot be used to probe for 
>> other submodules within the same top-level module you're in.
>> 
>> At the compiler driver level, a submodule is specified by giving a 
>> `-module-name` parameter with a dot in it. When a file is compiled, only the 
>> filenames of the other .swift files in the same module are specified, along 
>> with .o files for any submodules; then all the .o files within that 
>> submodule are linked into a single .o file for the whole submodule. So files 
>> in `ModKit.Foo` would be compiled with only the .swift files in `ModKit.Foo` 
>> and the .o file for `ModKit.Foo.Bar`; then all the `ModKit.Foo` .o files 
>> would be linked into one .o file for the top-level `ModKit` to use. None of 
>> `ModKit.Foo`'s .swift files would be included in the command line when 
>> compiling the top-level `ModKit` module.
>> 
>> (That bit is kind of speculative—particularly the idea of linking submodule 
>> files into a single .o file—but I think something like what I'm describing 
>> could work.)
>> 
>> Because the compiler driver is used to group submodules together, Xcode can 
>> specify submodules in project file metadata and calculate a submodule 
>> dependency graph, while SwiftPM can use folders and compile submodules 
>> whenever the compiler emits an error indicating that a file tried to import 
>> a nonexistent submodule. Other build systems can do whatever best suits 
>> their style.
>> 
>> ***
>> 
>> Thoughts?
>> 
>> -- 
>> Brent Royal-Gordon
>> Architechies
>> 
>> _______________________________________________
>> 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

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

Reply via email to