[swift-evolution] [Proposal] Qualified Imports and Modules

Pyry Jahkola pyry.jahkola at iki.fi
Wed Jul 20 04:41:02 CDT 2016

> On 19 Jul 2016, at 00:09, Robert Widmann via swift-evolution <swift-evolution at swift.org> wrote:
> TJ Usiyan, Harlan Haskins, and I have been working on a proposal to rework qualified imports and introduce an explicit module system to Swift that we’d like to publish for your viewing pleasure.
> The initial impetus was set out in a radar (rdar://17630570 <rdar://17630570>) I sent fairly early on that didn’t receive a response, so I started a swift-evolution <http://permalink.gmane.org/gmane.comp.lang.swift.evolution/1378> thread discussing the basics of this proposal.  It has been refined and expanded a bit to include an effort to make Swift modules explicit and updated with the feedback of that first thread.  Contents of the proposal are inline and can also be had as a gist <https://gist.github.com/CodaFi/42e5e5e94d857547abc381d9a9d0afd6> or on Github. <https://github.com/apple/swift-evolution/pull/440>

Firstly, thank you Robert, TJ, and Harlan for the proposal and especially for considering the introduction of submodules too! Those would be very useful when Swift libraries start growing. But maybe we can delay that discussion past Swift 3.

* * *

Secondly, I used to be in the selective import camp before, but I think it's somewhat counter to Swift's feel and direction where most of the API is wrapped inside types, and function names consist of not just the base name but argument labels too (even if we now have the "Gregorian" naming SE-0021 available). So while I don't see a big problem introducing something like

    import Foo.(fooConstant, bar(a:b) as bar1(a:b:), IrrelevantType as _)

to pick identifiers à la carte, I don't think it would be useful very often. And the alternative of accessing `bar` through the module name as `Foo.bar` does work just as well already.

…Well, that is except for the problem when the module name `Foo` happens to be shadowed by a non-module name `Foo`. I'd rather tackle that problem, and I do so as follows:

* * *

My usual pain points with imported (micro-)frameworks have been:

A) …when two modules (e.g. `Foo` and `Bar`) export the same identifier (`baz`) for different meanings.
B) …when a local or imported identifier (e.g. `class Tree`) has the same name as a module `Tree`.

In case A, I can just disambiguate by prefixing with the module name: `Foo.baz` vs. `Bar.baz`.

In case B, there is really no way around that I'm aware of. Anyway you write `Tree.foo` Swift will try to look up under the type `Tree` and never the module `Tree`.

Those two issues could be addressed by simply introducing qualified imports as follows:

    import Darwin as Darwin // qualified import (there could be a shorthand but what?)
    import UIKit as UI      // qualified import with renaming
    import Tree             // unqualified import brings in struct Tree.Tree, among others.
    import Tree as T        // qualified import
    let out = Darwin.stdout      // ok
    var vc: UI.UIViewController? // ok
    let tree: Tree = T.emptyTree // ok; `struct Tree.Tree` was imported unqualified
    let err = stderr
    // error: Use of unresolved identifier 'stderr'
    // note: did you mean 'Darwin.stderr'?
    var view: UIView?
    // error: Use of unresolved type 'UIView'
    // note: did you mean 'UI.UIView'?
    enum UI {}
    // error: invalid redeclaration of 'UI'
    // note: 'UI' previously declared (line 2)

The qualified import syntax, `import MODULE as NAME`, imports the module MODULE such that its contents can be accessed through the prefix of `NAME.` – but not without it, unless also imported without qualification.

The given NAME uses up that identifier in the current scope (as if it was a private type) such that there can be no locally visible type or value with the same name in that scope. For example, if the current module defined NAME publicly or internally in another file, then that identifier would be shadowed in this file by the qualified module name NAME.

Side note: It is still possible to chain multiple module imports, qualified or not, on the same line (albeit not a particularly good coding style if they're unrelated). The order of imports does not matter. So the above example could've been written as:

    import Darwin as Darwin, UIKit as UI, Tree as T, Tree

* * *

I think that's the small change we need most urgently. The rest can wait.

— Pyry

PS: Another thing I'd see useful, especially when migrating code from the `MyPlayground_Sources` module to a real project would be a syntax something like `_.foo` to access the `foo` that is visible in the current file top-level, so to escape any local shadowing.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160720/4924a32d/attachment.html>

More information about the swift-evolution mailing list