A request from me: if you use the word "submodule" to refer to any particular feature or design, please explain what you're using it for. People have a lot of different features they want in Swift that could be called "submodules", and there's not one agreed-upon design that people will understand when you say the word. I'll try to list the most common requests / proposals below. (Note that many proposed designs have combined more than one of these features. I've tried to decompose them for the purpose of this discussion, but they're not inherently independent.)
Namespaces
namespace Foo {
func foo() {}
}
namespace Foo {
func test() {
foo()
}
}
// Out here I have to qualify my reference, like a static function.
Foo.foo()
Namespaces come up fairly often as a request even without mentioning the word submodules. They're a way to group things and to keep from cluttering the top-level identifier space. Today the recommended idiom is to use a caseless enum:
enum Foo {}
extension Foo {
static func foo() {}
}
This has nearly the same effect, but isn't really distinguishable from a normal enum where the coder just hasn't added any cases yet, or a local version of Never
.
Some additional features namespaces could have:
-
private
would probably behave like it does in types, making a declaration only accessible within that namespace in the same file. -
C++ has a feature called
using
that allows you to use names from a particular namespace unqualified within a particular context. Other languages have similar features.
Access Control
A few people have started up discussion on this again, and so I don't want to get into it on this thread, but if you grouped a handful of files together and called that a submodule it might be useful to have an access control level between internal
and fileprivate
that means "these files, but not the entire module". This gets trickier if you allow nesting or overlapping of these groups, or if you try to do it with declarations rather than whole files, but there could be something here.
(Compare with Rust's access control model, where something is either "exported to the next level up" or "only exposed to me". There are additional features too but I get the impression those are less common.)
Dependencies
Since Swift's incremental build logic is still *cough* conservative, some people have proposed grouping files together for dependency analysis purposes. I'm not quite sure how this works; does it mean they don't rely on the rest of the module? Or does it mean that the rest of the module doesn't rely on them? Or is there some kind of "local import" to explicitly declare such dependencies? But it could be useful.
Explicit imports
A feature that Clang's modules have is the ability to separate off some APIs and only expose them if you spell out the full import path. The most common example of this is UIKit.UIGestureRecognizerSubclass. If you use import UIKit
, you get most of UIKit's submodules: UIKit.UIView, UIKit.UIGeometry, etc. (Most submodule names are inferred from the headers included by the umbrella header.) But you don't get the parts of UIGestureRecognizer needed to make a subclass, because most of the time they'd just be in your way. You only get them if you say import UIKit.UIGestureRecognizerSubclass
.
It's not exactly clear how you'd define these in Swift.
(Note that this wouldn't actually provide much safety because of other problems: today, extension methods are made visible everywhere if they're imported anywhere.)
Multiple modules, one target
The Swift compilation model says that all code in one module is compiled together. Sometimes people want "sub-target modules" instead: something that acts like a module at the language level, but which is still built into a single binary output. (Static libraries do most of this, especially if we formalize @_exported
, but not the optimization part.)
A related thing people want is to only expose certain declarations to other modules in the project / package. This is kind of "access control", kind of "explicit imports", and kind of "multiple modules, one target", so I'm sticking it here. (It's also almost never called "submodules", so maybe it doesn't belong in this post at all, but whatever.)
etc.
I think that covers the top features that people have called "submodules". If you're going to propose any of them, please be explicit about what you are and aren't including!
Some additional thoughts for any such proposal:
- Are you grouping files or declarations? If declarations, do all the declarations have to be in the same file or not?
- Can you nest submodules? Can one file or declaration be part of multiple submodules?
- What are the ABI and source compatibility implications?
- How does it interact with access control?
- What does it mean to have an extension of someone else's type in your submodule?
EDIT: See also The One Stop Shop for Previous Submodule Pitches.