I'm proposing (again) that we allow identifiers to contain whitespace and other non-identifier characters when surrounded by backticks (full pitch write-up). For example,
import `my/cool/project/ui/navigation`
@Test func `tapping pushes the nav stack`() { /* ... */ }
Now you might be thinking: this was proposed (and rejected) as SE-0275. In the four years since then, I'm hoping to revisit this based on both new information and continued lived experience. I've written a new proposal (I am not the original author; thank you to @adellibovi for their work on SE-0275!) to hopefully address the concerns that were raised by both the community and the Core Team that led to its rejection. To briefly summarize below:
Descriptive test naming
One of the primary motivating use cases was descriptive test naming, and one of the reasons for rejection was that the Core Team wanted to see a testing framework design emerge that provided a different way to attach a string to a test case. That framework has now emerged as swift-testing, but users wanting a descriptive test name are required to name the test twice:
@Test("tapping pushes the nav stack")
func tappingPushesTheNavStack() { /* ... */ }
This is redundant but also introduces inconsistency; test result reports and test UIs will show the descriptive names, but other tooling (debuggers, backtraces, index data for code navigation) will use the declaration name. I believe this is a worse resting spot than if we simply allowed the function name and the descriptive name to be the same (and for reasons I discuss further in the pitch write-up, I believe the overall design of swift-testing is correct and that it should not be fundamentally changed to tackle this problem in a different way).
Module naming
The use case I wrestle with the most is module naming. For massive codebases where a project might contain hundreds or more fine-grained modules, letting developers choose their own module names doesn't scale, nor does the flat namespace that we have for all Swift modules in general.
We (Bazel users) have adopted the convention of automatically deriving module names by mangling the path to the module's build target; for example, a target labeled //my/cool/project/ui:navigation
would be named my_cool_project_ui_navigation
. This is "fine" but it poses challenges for the humans writing those imports since they have to always transform the build target labels into the right module name. In these massive codebases, we also try to automate as much as we can involving dependency/import management via tooling. Since this transformation is not reversible, it makes writing such tooling more difficult.
Over the years I've experimented with other designs for this problem, but ultimately I keep coming back to the same spot: anything else I've come up with adds even more complexity to serialization and search path logic. The proposed solution (letting the build system provide module aliases that have non-identifier characters) is far simpler and cleaner (and Clang modules can already do this, if you eschew the @import
syntax and use path-based #include
s, so those come along for free).
The new proposal also tries to go into more detail about edge cases and interactions with tooling and possible future directions that were raised as concerns.
The full write-up can be found here. The write-up also includes links to a partial implementation. Thanks for taking a look!