Is the ability to type alias module names explicitly not supported or is it just something that hasn't been explored yet?
What would you use it for? Languages which support something like this typically have nested modules, but Swift doesn't have submodules, and the default import statement already brings in scope all visible items from the imported module.
I imagine that primary use case for this would be that you want to explicitly qualify the module name when referring to some declaration, to clarify where it is coming from, but the module's name is very long. But Swift modules generally have short names.
Naming conflicts when two imported frameworks share a class with the same name.
For a class, you can create local typealiases once and refer to them.
typealias FMyClass = ModuleF.MyClass
typealias GMyClass = ModuleG.MyClass
Arguably, this is less aesthetic than F.MyClass
and G.MyClass
but it gets the job done.
I know. What I want to know is what I asked in the first post.
Also reusing XCTests from different targets that use @testable import <module_name>
I just ran into a problem where I can't disambiguate the protocol SwiftUI.App from my module which is named App. I need to refer to my module, however, since I've also imported SwiftUI in the same file, I get the protocol instead of my App. If I could import SwiftUI.App as SwiftUIApp
then presumably referring to App.MyType would once again work. Alternatively being able to refer to <the_current_module>.MyType would be a solution e.g. import self.MyType
(where self refers to this module). Another option might be import module App
since you can already e.g. import protocol SwiftUI.App
to explicitly state you want that one.
Why can't you use <AppModule>.App
?
And I thought local types were supposed to take precedence in cases like this, but perhaps the rules are more complicated than that.
My <AppModule>
is named App
. So when I try to refer to it, there's ambiguity. I want App.ViewState
, but App
is also a protocol in SwiftUI
so I get the protocol not my App
module.
I think the suggestion here would be to simply rename your module, since your app name isn't actually App
. It's unlikely the language would be changed to support something that's more properly dealt with directly.
I think the suggestion here would be to simply rename your module, since your app name isn't actually
App
.
That is a solution, sure. But it's kinda akin to "you're doing it wrong". I don't think the language should have an opinion on my choice of naming. Consider that my app name might conflict with the name of a dependency so I can't simply use my app name either. Because of a language limitation, I essentially have to come up with a nonsense name. This is not ideal. Other languages support referring to the current module in order to disambiguate symbol paths. This is absolutely a minor shortcoming of Swift's design.
It's unlikely the language would be changed to support something that's more properly dealt with directly.
"more properly"? huh. I don't really think there's a proper way to enforce who gets which module names so the natural solution is to provide a mechanism to disambiguate, just like swift does with import <protocol, class, struct, etc.> Foo.Bar
. The fact that this is in the language in the first place seems to indicate that there's a need to disambiguate. Where we end up if we were to take your stance is that there can be no name conflicts ever, which is simply unenforceable and impractical.
It's also a little silly, consider someone who named their app first and then later wants to bring in a dependency of the same name. "Oh sorry you must be doing it wrong obviously rename your app..." Not a great UX.
The original point of this thread was a suggestion to enable typealiasing module names. Rather than doing that in your code, why not simply rename your module so it doesn't conflict and you don't need the modulealias
in the first place? Not only is that something simple you can do now, it would lead to better code. You haven't presented a very motivating use case.
The original point of this thread was a suggestion to enable typealiasing module names. Rather than doing that in your code, why not simply rename your module so it doesn't conflict and you don't need the
modulealias
in the first place?
Because renaming is not a solution to the problem. It only delays the problem and causes naming stink. And as I've subsequently demonstrated, there are 100% valid scenarios where a module name can become shadowed that you don't have control over, such as if you import some other module that isn't name the same as yours but which does have public type which shadows your module name. AFAIU it is impossible to disambiguate in that case because you can't say, "start at this module irrespective of the name and then look for the type Bar".
Not only is that something simple you can do now, it would lead to better code. You haven't presented a very motivating use case.
It is not obvious at all that encouraging a rename to MyNameThatReallyCantEverConflictSoHelpMeFoo.Bar
is preferable to import Bar from Foo
or import self.Bar
.
Honestly the fact that other languages solve this without really any additional complexity should be motivating enough. It's a very common problem to be dealing with two types that alias each other in the same source file. In most any other language when you run into that problem you can lookup the rules for importing code and easily find support for rebinding an imported name to another name for the scope of the source file and/or syntax for referencing type names absolutely. Go, Rust, Python, Ruby, Javascript, Kotlin, Java, Haskell, (to name some popular ones) all support either rebinding names at import time or importing unambiguously by absolute path, or both.
Given a current module Foo
importing OtherModule
, none of
import OtherModule.Foo as NotFoo
import Qux from self
import self.Qux
seem like they would do anything other than offer people flexibility without polluting the language aesthetically or cognitively and provide users an out when faced with a scenario where aliasing occurs.
The real solution here is a way to specify that “this particular identifier is fully qualified, meaning it begins with a module name”.
And yes, in the case of two modules with the same name, it would be useful to be able to import one as a different name.
I think the plan is roughly:
- Finalize unique package identifiers, possibly as part of the registry work, to ensure every package's module's can be uniquely referenced.
- Create special syntax for module disambiguation and reference.
Module::Type
is usually pitched, I hope for something else. - If still necessary, create a way for package consumers to change the module name of a package. I haven't seen much discussion yet, but I think I'd prefer a way for packages to declare the new name rather than it being part of the
import
statement. That way you get a single consistent name throughout your project.
These all seem rather low priority at the moment, but we'll see.
This is a really strange position. App
didn’t always exist in SwiftUI; when it was added, that was compatibility-breaking for any project that happened to have a module named App.
Of course, this applies to any new type added anywhere, so adding a new type to any public module is now a potentially source-breaking change that requires downstream consumers to rename their modules.
According to Jon’s analysis, those people are just wrong for having used module names that other people later want to use as type names.
(Adding conflicting type names can also break downstream consumers, but in that case we have a relatively low-impact workaround: use fully qualified names, possibly using a typealias.)
Perhaps we should adopt an Objective-C style namespacing approach for modules, where everyone puts their initials at the beginning of module names?
Please no to the initials approach for name spacing- it’s definitely not one of the missed features from objc…
Our use case for type-aliased modules:
We are generating our api calls via Open API spec, which results in a single file of generated structs and api functions. In this file there's many explicit references in order to eliminate disambiguation, i.e. OurMainModule.User
.
We just added a new target to our project, NotificationContent
and attempted to include our api code in its Target Membership. This results in compile-time errors for the new target, because OurMainModule.User
doesn't exist -- instead it is expecting NotificationContent.User
.
With module aliases this would be a one line fix:
modulealias OurMainModule = NotificationContent
Otherwise, we're actually stumped on how to fix the issue. We can't really duplicate the file because it's generated and will quickly become out of date. Maybe we can write some extra code to generate two separate files that each use the respective Module names, but this may not even be possible with the code gen system we're using.
You can put all your generated types in an enum “namespace”, and use that for qualification instead of the module name.
enum namespace doesn't work for protocols. you can underscore the protocol and use a typealias in the namespace, but this will cause the protocol and all its requirements and extension members to disappear from IDE/documentation tooling. :(