Beef up Imports


(Robert Widmann) #1

I opened a radar a while ago about improving the import syntax in Swift and haven’t received much of a response, so I’ll try to expand on my thoughts in there here.

I’ve always been in love with the way Agda does Modules and imports. The syntax is wonderfully natural, and the features it provides are surprisingly useful given their simplicity. Because Swift forbids the redeclaration of names between structs, enums, classes, and typealiases, I don’t believe that the existing qualified import syntax is needed. Therefore, I propose the introduction of 3 agda-esque operations for imports to replace the usual `import {func|typealias|struct|class|enum|etc.}` syntax:

  • import Foo using (bar, Baz, qux, corge, …)
  • import Foo hiding (bar, baz, qux, corge, …)
  • import Foo renaming (grault to garply, waldo to fred, …)

The first of these is used to introduce a limited set of identifiers into scope, the second to import all but the given identifiers, and the third to rename identifiers at the module level. Again, it should be obvious by uniqueness of identifiers what each one is referencing, so qualification of each identifier is unnecessary. If more context is needed, or more granularity, a Haskell-esque

import Foo {using|hiding|renaming} (Baz(..) [to Graft(..)])

could be in order for structs, classes, and enums, but I don’t anticipate it will be of much use because individual members are already qualified by their containing structure or instance variable anyway.

What do you think?

~Robert Widmann


(Pyry Jahkola) #2

Therefore, I propose the introduction of 3 agda-esque operations for imports to replace the usual `import {func|typealias|struct|class|enum|etc.}` syntax:

  • import Foo using (bar, Baz, qux, corge, …)
  • import Foo hiding (bar, baz, qux, corge, …)
  • import Foo renaming (grault to garply, waldo to fred, …)

+1, but why not just…?

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

I've been able to work around identifier conflicts with the present import syntax but it gets clumsy with larger modules. I think both import-using and import-hiding would be very welcome in Swift, but the third notation made me double check its semantics from The Agda Wiki. Turns out <http://wiki.portal.chalmers.se/agda/pmwiki.php?n=ReferenceManual.Modules#mods> the renaming (...) syntax is actually an extension to the other two and is often used as a part of the one or the other:

import Foo using (bar, Baz, qux) renaming (waldo to fred)
import Foo hiding (bar, Baz, qux) renaming (waldo to fred)

Why not do without renaming? Just allow both import-using and import-hiding to optionally rename imported identifiers. Further, while these keywords can and should be made context-specific, I think it's simplest to reuse "as" as the associating keyword.

That would lead us to the proposed syntax:

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

where the tuple-like parentheses enclose a potentially empty list of identifiers or "identifier as identifier" mappings. The examples below should clarify the meaning and intended use of these two statements.

Again, it should be obvious by uniqueness of identifiers what each one is referencing, so qualification of each identifier is unnecessary.

Agreed. In addition, extending the import statement to class/enum/struct members doesn't make sense to me. Those don't conflict with the global scope at all.

— Pyry Jahkola

P.S. If this turns into a formal proposal, should we also address how imported operators should be dealt with? Can operators be selectively imported, hidden, or renamed? If renaming is made possible, we should be explicit on how the renamed operator inherits its associativity and precedence from the original one.

Examples

Suppose the module Foo contains the following identifiers:

// module Foo:
// - bar, Baz, qux, waldo

The "import-all" remains as it is in Swift 2:

import Foo

_ = (bar, Baz.self, qux, waldo) // All identifiers can be unqualified.

The "import-all" is synonymous with the longer "hide-nothing":

import Foo hiding ()

To hide or rename an identifier, fill in the parentheses:

import Foo hiding (Baz, waldo as fred)

_ = (bar, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.waldo) // These names must be qualified.
_ = Foo.fred // Compiler error!

To import selectively, potentially renaming identifiers, use the import-using syntax:

import Foo using (Baz as FooBaz, qux, waldo as fred)

_ = (FooBaz.self, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.bar) // These names must be qualified.

Finally, it's possible to require qualified use of the Foo module:

import Foo using ()

_ = (Foo.bar, Foo.Baz.self, Foo.qux, Foo.waldo) // OK.
_ = (bar, Baz.self, qux, waldo) // Compiler error x 4!

End.

···

On 27 Dec 2015, at 07:12, Developer via swift-evolution <swift-evolution@swift.org> wrote:


(Chris Lattner) #3

+1 for this general direction, if not this specific syntax. It would also be great to have import integrate with SPM so you can import a package from an SCM URL. This would be pretty handy in particular for #! scripts.

-Chris

···

On Dec 26, 2015, at 9:12 PM, Developer via swift-evolution <swift-evolution@swift.org> wrote:

I opened a radar a while ago about improving the import syntax in Swift and haven’t received much of a response, so I’ll try to expand on my thoughts in there here.

I’ve always been in love with the way Agda does Modules and imports. The syntax is wonderfully natural, and the features it provides are surprisingly useful given their simplicity. Because Swift forbids the redeclaration of names between structs, enums, classes, and typealiases, I don’t believe that the existing qualified import syntax is needed. Therefore, I propose the introduction of 3 agda-esque operations for imports to replace the usual `import {func|typealias|struct|class|enum|etc.}` syntax:

  • import Foo using (bar, Baz, qux, corge, …)
  • import Foo hiding (bar, baz, qux, corge, …)
  • import Foo renaming (grault to garply, waldo to fred, …)

The first of these is used to introduce a limited set of identifiers into scope, the second to import all but the given identifiers, and the third to rename identifiers at the module level. Again, it should be obvious by uniqueness of identifiers what each one is referencing, so qualification of each identifier is unnecessary. If more context is needed, or more granularity, a Haskell-esque


(TJ Usiyan) #4

*import* Foo *using* (bar, Baz, qux, waldo *as* fred) // a "whitelist
import"
*import* Foo *hiding* (bar, Baz, qux, waldo *as* fred) // a "blacklist
import"

Is nice but `hiding… (waldo as fred)` is confusing. Am I hiding waldo? It
is also strange to look in that list for things that I am hiding and things
that I am importing. Context switches after the item that I am importing
because `as` follows the item in question.

*import* Foo (waldo *as* fred) *using* (bar, Baz, qux) // a "whitelist
import"
*import* Foo (waldo *as* fred) *hiding* (bar, Baz, qux) // a "blacklist
import"

`using` didn't actually need the syntax change but maintaining one
consistent form might be worth it. If not, `import Foo using (bar, Baz,
waldo as fred)` is fine.

···

On Sun, Dec 27, 2015 at 7:44 AM, Pyry Jahkola via swift-evolution < swift-evolution@swift.org> wrote:

On 27 Dec 2015, at 07:12, Developer via swift-evolution < > swift-evolution@swift.org> wrote:

Therefore, I propose the introduction of 3 agda-esque operations for
imports to replace the usual `import
{func|typealias|struct|class|enum|etc.}` syntax:

• import Foo using (bar, Baz, qux, corge, …)
• import Foo hiding (bar, baz, qux, corge, …)
• import Foo renaming (grault to garply, waldo to fred, …)

+1, but why not just…?

*import* Foo *using* (bar, Baz, qux, waldo *as* fred) // a "whitelist
import"

*import* Foo *hiding* (bar, Baz, qux, waldo *as* fred) // a "blacklist
import"

I've been able to work around identifier conflicts with the present
*import* syntax but it gets clumsy with larger modules. I think both
*import*-*using* and *import*-*hiding* would be very welcome in Swift,
but the third notation made me double check its semantics from The Agda
Wiki. Turns out
<http://wiki.portal.chalmers.se/agda/pmwiki.php?n=ReferenceManual.Modules#mods>
the *renaming* (...) syntax is actually an extension to the other two
and is often used as a part of the one or the other:

*import* Foo *using* (bar, Baz, qux) *renaming* (waldo *to* fred)
*import* Foo *hiding* (bar, Baz, qux) *renaming* (waldo *to* fred)

Why not do without *renaming*? Just allow both *import*-*using* and
*import*-*hiding* to optionally *rename* imported identifiers. Further,
while these keywords can and should be made context-specific, I think it's
simplest to reuse "*as*" as the associating keyword.

That would lead us to the *proposed syntax*:

*import* Foo *using* (bar, Baz, qux, waldo *as* fred) // a "whitelist
import"

*import* Foo *hiding* (bar, Baz, qux, waldo *as* fred) // a "blacklist
import"

where the tuple-like parentheses enclose a potentially empty list of
identifiers or "identifier *as* identifier" mappings. The examples below
should clarify the meaning and intended use of these two statements.

Again, it should be obvious by uniqueness of identifiers what each one is
referencing, so qualification of each identifier is unnecessary.

Agreed. In addition, extending the import statement to class/enum/struct
members doesn't make sense to me. Those don't conflict with the global
scope at all.

— Pyry Jahkola

P.S. If this turns into a formal proposal, should we also address how
imported operators should be dealt with? Can operators be selectively
imported, hidden, or renamed? If renaming is made possible, we should be
explicit on how the renamed operator inherits its associativity and
precedence from the original one.

*Examples*

Suppose the module Foo contains the following identifiers:

// module Foo:
// - bar, Baz, qux, waldo

The "import-all" remains as it is in Swift 2:

*import* Foo

_ = (bar, Baz.self, qux, waldo) // All identifiers can be unqualified.

The "import-all" is synonymous with the longer "hide-nothing":

*import* Foo *hiding* ()

To hide or rename an identifier, fill in the parentheses:

*import* Foo *hiding* (Baz, waldo *as* fred)

_ = (bar, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.waldo) // These names must be qualified.
_ = Foo.fred // Compiler error!

To import selectively, potentially renaming identifiers, use the *import*-
*using* syntax:

*import* Foo *using* (Baz *as* FooBaz, qux, waldo *as* fred)

_ = (FooBaz.self, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.bar) // These names must be qualified.

Finally, it's possible to require qualified use of the Foo module:

*import* Foo *using* ()

_ = (Foo.bar, Foo.Baz.self, Foo.qux, Foo.waldo) // OK.
_ = (bar, Baz.self, qux, waldo) // Compiler error x 4!

End.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #5

The 'except' change is interesting, but it's still a context switch that can wind up confusing people. I think that's partly why Agda has the 'renaming' operation separate in the first place.

~Robert Widmann

2015/12/27 10:30、T.J. Usiyan via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

Is nice but `hiding… (waldo as fred)` is confusing. Am I hiding waldo? It is also strange to look in that list for things that I am hiding and things that I am importing. Context switches after the item that I am importing because `as` follows the item in question.

import Foo (waldo as fred) using (bar, Baz, qux) // a "whitelist import"
import Foo (waldo as fred) hiding (bar, Baz, qux) // a "blacklist import"

`using` didn't actually need the syntax change but maintaining one consistent form might be worth it. If not, `import Foo using (bar, Baz, waldo as fred)` is fine.

On Sun, Dec 27, 2015 at 7:44 AM, Pyry Jahkola via swift-evolution <swift-evolution@swift.org> wrote:

On 27 Dec 2015, at 07:12, Developer via swift-evolution <swift-evolution@swift.org> wrote:

Therefore, I propose the introduction of 3 agda-esque operations for imports to replace the usual `import {func|typealias|struct|class|enum|etc.}` syntax:

  • import Foo using (bar, Baz, qux, corge, …)
  • import Foo hiding (bar, baz, qux, corge, …)
  • import Foo renaming (grault to garply, waldo to fred, …)

+1, but why not just…?

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

I've been able to work around identifier conflicts with the present import syntax but it gets clumsy with larger modules. I think both import-using and import-hiding would be very welcome in Swift, but the third notation made me double check its semantics from The Agda Wiki. Turns out the renaming (...) syntax is actually an extension to the other two and is often used as a part of the one or the other:

import Foo using (bar, Baz, qux) renaming (waldo to fred)
import Foo hiding (bar, Baz, qux) renaming (waldo to fred)

Why not do without renaming? Just allow both import-using and import-hiding to optionally rename imported identifiers. Further, while these keywords can and should be made context-specific, I think it's simplest to reuse "as" as the associating keyword.

That would lead us to the proposed syntax:

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

where the tuple-like parentheses enclose a potentially empty list of identifiers or "identifier as identifier" mappings. The examples below should clarify the meaning and intended use of these two statements.

Again, it should be obvious by uniqueness of identifiers what each one is referencing, so qualification of each identifier is unnecessary.

Agreed. In addition, extending the import statement to class/enum/struct members doesn't make sense to me. Those don't conflict with the global scope at all.

— Pyry Jahkola

P.S. If this turns into a formal proposal, should we also address how imported operators should be dealt with? Can operators be selectively imported, hidden, or renamed? If renaming is made possible, we should be explicit on how the renamed operator inherits its associativity and precedence from the original one.

Examples

Suppose the module Foo contains the following identifiers:

// module Foo:
// - bar, Baz, qux, waldo

The "import-all" remains as it is in Swift 2:

import Foo

_ = (bar, Baz.self, qux, waldo) // All identifiers can be unqualified.

The "import-all" is synonymous with the longer "hide-nothing":

import Foo hiding ()

To hide or rename an identifier, fill in the parentheses:

import Foo hiding (Baz, waldo as fred)

_ = (bar, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.waldo) // These names must be qualified.
_ = Foo.fred // Compiler error!

To import selectively, potentially renaming identifiers, use the import-using syntax:

import Foo using (Baz as FooBaz, qux, waldo as fred)

_ = (FooBaz.self, qux, fred) // These names can be unqualified.
_ = (Foo.Baz.self, Foo.bar) // These names must be qualified.

Finally, it's possible to require qualified use of the Foo module:

import Foo using ()

_ = (Foo.bar, Foo.Baz.self, Foo.qux, Foo.waldo) // OK.
_ = (bar, Baz.self, qux, waldo) // Compiler error x 4!

End.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Pyry Jahkola) #6

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo hiding (bar, Baz, qux, waldo as fred) // a "blacklist import"

Is nice but `hiding… (waldo as fred)` is confusing. Am I hiding waldo?

You're right That's also what Robert pointed out in his reply. The way I thought it is "hide waldo, naming it as fred instead." I like the symmetry of it with the `import-using` statement, but true, it doesn't read well.

Hmm, given that keywords are still up in the air, why not s/hiding/except/? Then we'd have:

import Foo using (bar, Baz, qux, waldo as fred) // a "whitelist import"
import Foo except (bar, Baz, qux, waldo as fred) // a "blacklist import"

The double meaning of "except" in the phrases "except X" (without X) and "except X as Y" (everything else as is, but X as Y) would work.

And no one would write the silly sounding empty `import Foo except ()` anyway because the plain `import Foo` works too.

It is also strange to look in that list for things that I am hiding and things that I am importing. Context switches after the item that I am importing because `as` follows the item in question.

I'd say that's entirely ok. You could by convention e.g. keep a habit of listing all renames together (in the end of the list, or in another import-except statement), which could be checked by a linter if someone wanted.

···

T.J. Usiyan <griotspeak@gmail.com> wrote:


(Lily Ballard) #7

I like the idea here, but I'm not sold on the syntax. I also do
explicitly want an `import qualified`. And with qualified imports, I
question whether we really need to support renaming in the import
syntax here.

I'm tempted to say we should just crib Haskell's import rules
(https://wiki.haskell.org/Import), with the minor change that importing
members by name still makes them accessible via the module name too (in
Haskell `import Mod (x, y)` makes `x` and `y` visible but does not make
`Mod.x` or `Mod.y` visible). This lets you say things like

*import* Mod // imports Mod and all its members *import* Mod () // only
provides access to protocol conformances declared in Mod, doesn't
actually *import* anything *import* Mod *(*x,y*)* // imports `x` and
`y`, which are also accessible as e.g. `Mod.x`, but does not provide `z`
or `Mod.z` *import qualified* Mod // imports Mod but all access to
members has to go through it, e.g. `Mod.x` *import qualified* Mod
*(*x,y*)* // imports Mod but only provides access to `Mod.x` and `Mod.y`
but not e.g. `Mod.z` *import *Mod *hiding* *(*x,y*)* // imports Mod and
its members except for `x` or `y` *import qualified* Mod *hiding*
*(*x,y*)* // imports e.g. `Mod.z` but not `Mod.x` or `Mod.y`* * *import*
Mod *as* Foo // imports Mod and renames the module to Foo, so e.g. `x`
and `Foo.x` are accessible *import* Mod *as* Foo *(*x,y*)* // renames
Mod to Foo, provides `x`, `y`, `Foo.x`, and `Foo.y` *import qualified*
Mod *as* Foo // renames Mod to Foo, all members are accessible via the
module e.g. `Foo.x` *import qualified* Mod *as* Foo *(*x,y*)* // renames
Mod to Foo, provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same
module, so you can say something like

*import qualified* Mod *import* Mod *(*x,y*)*

to provide access to all of Mod qualified with the name, and
additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard


(David Waite) #8

This seems like it would strongly motivate toward a (possibly directory-scoped or system-scoped) package cache. Not saying thats a bad thing, just that it seems different than the executable packaging Swift has today with “Apps".

-DW

···

On Dec 28, 2015, at 5:07 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

+1 for this general direction, if not this specific syntax. It would also be great to have import integrate with SPM so you can import a package from an SCM URL. This would be pretty handy in particular for #! scripts.


(TJ Usiyan) #9

+1 in general.

As an aside (In response to David), I have wanted the ability to import
'submodules' from a framework since Swift 1.0. Being able to group related
functionality to import is a desirable feature, IMO
TJ

···

On Mon, Dec 28, 2015 at 7:01 PM, David Waite via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 28, 2015, at 5:07 PM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

+1 for this general direction, if not this specific syntax. It would also
be great to have import integrate with SPM so you can import a package from
an SCM URL. This would be pretty handy in particular for #! scripts.

This seems like it would strongly motivate toward a (possibly
directory-scoped or system-scoped) package cache. Not saying thats a bad
thing, just that it seems different than the executable packaging Swift has
today with “Apps".

-DW

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Thorsten Seitz) #10

+1

-Thorsten

···

Am 29.12.2015 um 01:47 schrieb Kevin Ballard via swift-evolution <swift-evolution@swift.org>:

I like the idea here, but I'm not sold on the syntax. I also do explicitly want an `import qualified`. And with qualified imports, I question whether we really need to support renaming in the import syntax here.

I'm tempted to say we should just crib Haskell's import rules (https://wiki.haskell.org/Import), with the minor change that importing members by name still makes them accessible via the module name too (in Haskell `import Mod (x, y)` makes `x` and `y` visible but does not make `Mod.x` or `Mod.y` visible). This lets you say things like

import Mod // imports Mod and all its members
import Mod () // only provides access to protocol conformances declared in Mod, doesn't actually import anything
import Mod (x,y) // imports `x` and `y`, which are also accessible as e.g. `Mod.x`, but does not provide `z` or `Mod.z`
import qualified Mod // imports Mod but all access to members has to go through it, e.g. `Mod.x`
import qualified Mod (x,y) // imports Mod but only provides access to `Mod.x` and `Mod.y` but not e.g. `Mod.z`
import Mod hiding (x,y) // imports Mod and its members except for `x` or `y`
import qualified Mod hiding (x,y) // imports e.g. `Mod.z` but not `Mod.x` or `Mod.y`
import Mod as Foo // imports Mod and renames the module to Foo, so e.g. `x` and `Foo.x` are accessible
import Mod as Foo (x,y) // renames Mod to Foo, provides `x`, `y`, `Foo.x`, and `Foo.y`
import qualified Mod as Foo // renames Mod to Foo, all members are accessible via the module e.g. `Foo.x`
import qualified Mod as Foo (x,y) // renames Mod to Foo, provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same module, so you can say something like

import qualified Mod
import Mod (x,y)

to provide access to all of Mod qualified with the name, and additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jordan Rose) #11

I'm (personally) not such a fan of saying "import just this one thing" or "import everything except this one thing"—you end up trying to use UIImage(named:), and then realizing you haven't imported any of the top-level names. But "import qualified" seems great to me, and "import Foo as Bar" also seems reasonable.

Jordan

···

On Dec 28, 2015, at 16:47, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I like the idea here, but I'm not sold on the syntax. I also do explicitly want an `import qualified`. And with qualified imports, I question whether we really need to support renaming in the import syntax here.

I'm tempted to say we should just crib Haskell's import rules (https://wiki.haskell.org/Import), with the minor change that importing members by name still makes them accessible via the module name too (in Haskell `import Mod (x, y)` makes `x` and `y` visible but does not make `Mod.x` or `Mod.y` visible). This lets you say things like

import Mod // imports Mod and all its members
import Mod () // only provides access to protocol conformances declared in Mod, doesn't actually import anything
import Mod (x,y) // imports `x` and `y`, which are also accessible as e.g. `Mod.x`, but does not provide `z` or `Mod.z`
import qualified Mod // imports Mod but all access to members has to go through it, e.g. `Mod.x`
import qualified Mod (x,y) // imports Mod but only provides access to `Mod.x` and `Mod.y` but not e.g. `Mod.z`
import Mod hiding (x,y) // imports Mod and its members except for `x` or `y`
import qualified Mod hiding (x,y) // imports e.g. `Mod.z` but not `Mod.x` or `Mod.y`
import Mod as Foo // imports Mod and renames the module to Foo, so e.g. `x` and `Foo.x` are accessible
import Mod as Foo (x,y) // renames Mod to Foo, provides `x`, `y`, `Foo.x`, and `Foo.y`
import qualified Mod as Foo // renames Mod to Foo, all members are accessible via the module e.g. `Foo.x`
import qualified Mod as Foo (x,y) // renames Mod to Foo, provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same module, so you can say something like

import qualified Mod
import Mod (x,y)

to provide access to all of Mod qualified with the name, and additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Simon Pilkington) #12

I like the fact that this would retain simplicity for basic uses - developers could continue to use 'import Mod' and not even know about the more advanced syntax unless they need that power. The additional syntax seems like a natural progression from the base case.

Kevin, I understand the motivation for not really needed renaming for qualified imports but it feels like we would still need them for unqualified/global ones. Do you think this is a valid use case? As suggested previously I think this would be least confusing/ambiguous by using a seperate renaming syntax -
import Mod hiding (x,y) renaming (z as zz)

An import statement such as above could equally be handled by seperate imports - one for hiding and one for renaming - and it might be worth being flexible and support both styles.

+1 to submodules as well, particularly if integrated into SPM to help optimise/reduce compile times when a module depends only on part of an otherwise related group of functionality that should be vended/consumed/versioned together.

-Simon

···

On 29 Dec 2015, at 11:47 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I like the idea here, but I'm not sold on the syntax. I also do explicitly want an `import qualified`. And with qualified imports, I question whether we really need to support renaming in the import syntax here.

I'm tempted to say we should just crib Haskell's import rules (https://wiki.haskell.org/Import), with the minor change that importing members by name still makes them accessible via the module name too (in Haskell `import Mod (x, y)` makes `x` and `y` visible but does not make `Mod.x` or `Mod.y` visible). This lets you say things like

import Mod // imports Mod and all its members
import Mod () // only provides access to protocol conformances declared in Mod, doesn't actually import anything
import Mod (x,y) // imports `x` and `y`, which are also accessible as e.g. `Mod.x`, but does not provide `z` or `Mod.z`
import qualified Mod // imports Mod but all access to members has to go through it, e.g. `Mod.x`
import qualified Mod (x,y) // imports Mod but only provides access to `Mod.x` and `Mod.y` but not e.g. `Mod.z`
import Mod hiding (x,y) // imports Mod and its members except for `x` or `y`
import qualified Mod hiding (x,y) // imports e.g. `Mod.z` but not `Mod.x` or `Mod.y`
import Mod as Foo // imports Mod and renames the module to Foo, so e.g. `x` and `Foo.x` are accessible
import Mod as Foo (x,y) // renames Mod to Foo, provides `x`, `y`, `Foo.x`, and `Foo.y`
import qualified Mod as Foo // renames Mod to Foo, all members are accessible via the module e.g. `Foo.x`
import qualified Mod as Foo (x,y) // renames Mod to Foo, provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same module, so you can say something like

import qualified Mod
import Mod (x,y)

to provide access to all of Mod qualified with the name, and additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #13

How is this done for things like the Darwin module?

~Robert Widmann

2015/12/28 20:19、T.J. Usiyan via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

+1 in general.

As an aside (In response to David), I have wanted the ability to import 'submodules' from a framework since Swift 1.0. Being able to group related functionality to import is a desirable feature, IMO
TJ

On Mon, Dec 28, 2015 at 7:01 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 28, 2015, at 5:07 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

+1 for this general direction, if not this specific syntax. It would also be great to have import integrate with SPM so you can import a package from an SCM URL. This would be pretty handy in particular for #! scripts.

This seems like it would strongly motivate toward a (possibly directory-scoped or system-scoped) package cache. Not saying thats a bad thing, just that it seems different than the executable packaging Swift has today with “Apps".

-DW

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #14

The reason why I say that we don't really need renaming is because you
only need renaming in order to avoid conflicts, and you can avoid
conflicts just by using a qualified import instead. As an example, if I
want to import some library that declares its own Result type, but I of
course have my own Result type as well, then I might say something like

import FrobLib hiding (Result) import qualified FrobLib (Result)

This will get me all of FrobLib except for Result, and also let me use
`FrobLib.Result` whenever I do need to reference that library's Result
type. Or if I need to reference it frequently I might even say

import qualified FrobLib as F (Result)

so I can just use `F.Result`.

In my experience, there's not much need for renaming once you can do
this with qualified imports.

-Kevin Ballard

···

On Tue, Jan 5, 2016, at 05:44 PM, Simon Pilkington wrote:

I like the fact that this would retain simplicity for basic uses -
developers could continue to use '*import* Mod' and not even know
about the more advanced syntax unless they need that power. The
additional syntax seems like a natural progression from the base case.

Kevin, I understand the motivation for not really needed renaming for
qualified imports but it feels like we would still need them for
unqualified/global ones. Do you think this is a valid use case? As
suggested previously I think this would be least confusing/ambiguous
by using a seperate *renaming* syntax - *import* Mod *hiding* (x,y)
*renaming* (z as zz)

An import statement such as above could equally be handled by seperate
imports - one for hiding and one for renaming - and it might be worth
being flexible and support both styles.

+1 to submodules as well, particularly if integrated into SPM to help
optimise/reduce compile times when a module depends only on part of an
otherwise related group of functionality that should be
vended/consumed/versioned together.

-Simon

On 29 Dec 2015, at 11:47 AM, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> wrote:

I like the idea here, but I'm not sold on the syntax. I also do
explicitly want an `import qualified`. And with qualified imports, I
question whether we really need to support renaming in the import
syntax here.

I'm tempted to say we should just crib Haskell's import rules
(https://wiki.haskell.org/Import), with the minor change that
importing members by name still makes them accessible via the module
name too (in Haskell `import Mod (x, y)` makes `x` and `y` visible
but does not make `Mod.x` or `Mod.y` visible). This lets you say
things like

*import* Mod // imports Mod and all its members *import* Mod () //
only provides access to protocol conformances declared in Mod,
doesn't actually *import* anything *import* Mod *(*x,y*)* // imports
`x` and `y`, which are also accessible as e.g. `Mod.x`, but does not
provide `z` or `Mod.z` *import qualified* Mod // imports Mod but all
access to members has to go through it, e.g. `Mod.x` *import
qualified* Mod *(*x,y*)* // imports Mod but only provides access to
`Mod.x` and `Mod.y` but not e.g. `Mod.z` *import *Mod *hiding*
*(*x,y*)* // imports Mod and its members except for `x` or `y`
*import qualified* Mod *hiding* *(*x,y*)* // imports e.g. `Mod.z` but
not `Mod.x` or `Mod.y`** *import* Mod *as* Foo // imports Mod and
renames the module to Foo, so e.g. `x` and `Foo.x` are accessible
*import* Mod *as* Foo *(*x,y*)* // renames Mod to Foo, provides `x`,
`y`, `Foo.x`, and `Foo.y` *import qualified* Mod *as* Foo // renames
Mod to Foo, all members are accessible via the module e.g. `Foo.x`
*import qualified* Mod *as* Foo *(*x,y*)* // renames Mod to Foo,
provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same
module, so you can say something like

*import qualified* Mod *import* Mod *(*x,y*)*

to provide access to all of Mod qualified with the name, and
additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #15

Clang modules have always supported submodules, and in fact when you
import an Obj-C framework (that doesn't use a completely custom
module.modulemap), each header from the framework is automatically a
submodule (and importing the framework automatically imports all the
submodules).

All Swift is missing is a way to express a submodule hierarchy in Swift
code. It already supports the idea of importing submodules, just not
declaring them.

-Kevin

How is this done for things like the Darwin module?

~Robert Widmann

2015/12/28 20:19、T.J. Usiyan via swift-evolution <swift-
evolution@swift.org> のメッセージ:

+1 in general.

As an aside (In response to David), I have wanted the ability to
import 'submodules' from a framework since Swift 1.0. Being able to
group related functionality to import is a desirable feature, IMO TJ

+1 for this general direction, if not this specific syntax. It
would also be great to have import integrate with SPM so you can
import a package from an SCM URL. This would be pretty handy in
particular for #! scripts.

This seems like it would strongly motivate toward a (possibly directory-
scoped or system-scoped) package cache. Not saying thats a bad
thing, just that it seems different than the executable packaging
Swift has today with “Apps".

-DW

_______________________________________________

swift-evolution mailing list

···

On Tue, Dec 29, 2015, at 12:29 PM, Developer via swift-evolution wrote:

On Mon, Dec 28, 2015 at 7:01 PM, David Waite via swift-evolution <swift- >> evolution@swift.org> wrote:

On Dec 28, 2015, at 5:07 PM, Chris Lattner via swift-evolution <swift- >>>> evolution@swift.org> wrote:

swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #16

I'll admit, Agda does seem to use 'renaming' to enable notational preference first and foremost. I'm not sure of too many Swift authors who would put something like that too high on their list of concerns, so +1. Drop renaming.

~Robert Widmann

2016/01/07 1:53、Kevin Ballard via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

The reason why I say that we don't really need renaming is because you only need renaming in order to avoid conflicts, and you can avoid conflicts just by using a qualified import instead. As an example, if I want to import some library that declares its own Result type, but I of course have my own Result type as well, then I might say something like

import FrobLib hiding (Result)
import qualified FrobLib (Result)

This will get me all of FrobLib except for Result, and also let me use `FrobLib.Result` whenever I do need to reference that library's Result type. Or if I need to reference it frequently I might even say

import qualified FrobLib as F (Result)

so I can just use `F.Result`.

In my experience, there's not much need for renaming once you can do this with qualified imports.

-Kevin Ballard

On Tue, Jan 5, 2016, at 05:44 PM, Simon Pilkington wrote:
I like the fact that this would retain simplicity for basic uses - developers could continue to use 'import Mod' and not even know about the more advanced syntax unless they need that power. The additional syntax seems like a natural progression from the base case.

Kevin, I understand the motivation for not really needed renaming for qualified imports but it feels like we would still need them for unqualified/global ones. Do you think this is a valid use case? As suggested previously I think this would be least confusing/ambiguous by using a seperate renaming syntax -
import Mod hiding (x,y) renaming (z as zz)

An import statement such as above could equally be handled by seperate imports - one for hiding and one for renaming - and it might be worth being flexible and support both styles.

+1 to submodules as well, particularly if integrated into SPM to help optimise/reduce compile times when a module depends only on part of an otherwise related group of functionality that should be vended/consumed/versioned together.

-Simon

On 29 Dec 2015, at 11:47 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

I like the idea here, but I'm not sold on the syntax. I also do explicitly want an `import qualified`. And with qualified imports, I question whether we really need to support renaming in the import syntax here.

I'm tempted to say we should just crib Haskell's import rules (https://wiki.haskell.org/Import), with the minor change that importing members by name still makes them accessible via the module name too (in Haskell `import Mod (x, y)` makes `x` and `y` visible but does not make `Mod.x` or `Mod.y` visible). This lets you say things like

import Mod // imports Mod and all its members
import Mod () // only provides access to protocol conformances declared in Mod, doesn't actually import anything
import Mod (x,y) // imports `x` and `y`, which are also accessible as e.g. `Mod.x`, but does not provide `z` or `Mod.z`
import qualified Mod // imports Mod but all access to members has to go through it, e.g. `Mod.x`
import qualified Mod (x,y) // imports Mod but only provides access to `Mod.x` and `Mod.y` but not e.g. `Mod.z`
import Mod hiding (x,y) // imports Mod and its members except for `x` or `y`
import qualified Mod hiding (x,y) // imports e.g. `Mod.z` but not `Mod.x` or `Mod.y`
import Mod as Foo // imports Mod and renames the module to Foo, so e.g. `x` and `Foo.x` are accessible
import Mod as Foo (x,y) // renames Mod to Foo, provides `x`, `y`, `Foo.x`, and `Foo.y`
import qualified Mod as Foo // renames Mod to Foo, all members are accessible via the module e.g. `Foo.x`
import qualified Mod as Foo (x,y) // renames Mod to Foo, provides access to `Foo.x` and `Foo.y` but not e.g. `Foo.z`

Furthermore, you can have multiple import statements from the same module, so you can say something like

import qualified Mod
import Mod (x,y)

to provide access to all of Mod qualified with the name, and additionally import `x` and `y` as unqualified identifiers.

-Kevin Ballard

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Pyry Jahkola) #17

I tend to also agree. One more reason not to rename imported identifiers is that it makes them less googleable at call site; there's an extra human indirection (looking up the respective import statement) before being successfully at searching with the commonly known name.

But is there more we could prune away from this proposal? When would you really want to hide or selectively choose any qualified imports? If qualified imports always imported all identifiers, with the restriction that they must be qualified, would there (should there) ever be conflicts with anything else imported or locally defined?

It seems to me we could simplify it all down to just:

import Lib // Works just like in Swift 2, qualified or not.
import Lib (Foo, bar) // Only Foo and bar unqualified, others like Lib.qux.
import Lib () // Everything must be qualified: Lib.Foo etc.
import Lib hiding (qux) // Everything but baz works unqualified, and all like Lib.qux.
import Lib as L // Import all unqualified, allow qualification under L like L.Foo.
import Lib as L (Foo) // Only Foo unqualified, others under L like L.bar.
import Lib as L () // Everything must be qualified under L like L.Foo etc.
import Lib as L hiding (qux) // ..., qux must be qualified like L.qux.

Wouldn't that work pretty nicely, with minimal additions to syntax?

— Pyry

P.S. Okay, I can come up with a somewhat contrived corner case: What to do with modules whose names collide with type names imported unqualified, e.g. https://github.com/robrix/Either ?

import Lib // defines bar like above (1)

struct Lib {
    static func bar() {} // (2)
}

Lib.bar() // Which one gets called, (1) or (2)?

One might suggest the above call resolved to the locally defined function. How about if struct Lib was defined in another module that was also imported?

···

On 07 Jan 2016, at 11:59, Developer via swift-evolution <swift-evolution@swift.org> wrote:

I'll admit, Agda does seem to use 'renaming' to enable notational preference first and foremost. I'm not sure of too many Swift authors who would put something like that too high on their list of concerns, so +1. Drop renaming.