qsharp
qsharp copied to clipboard
Allow implicit top-level namespaces when parsing files
This PR allows for implicit top-level namespaces.
Prior to this PR, all Q# had to be wrapped in namespace declarations:
namespace Foo {
operation Main() : Unit {}
}
Now, users can optionally omit the top-level namespace declaration. This is nice, because it removes a level of necessary indentation.
If the explicit namespace declaration is omitted, the file path is used to determine the namespace name. For example, in ~/code/qsharp/project_dir/src/Foo/Bar/Baz.qs
, the file:
operation Quux() : Unit {}
defines a callable in the namespace Foo.Bar.Baz
.
We detect the "root" of the project by calculating the largest common prefix of the file paths. This is the most versatile approach, in my opinion, without having to introduce project-based logic into the compiler itself (that is, add a qsc_project
dependency to qsc_frontend
-- which would be cyclical via qsc_lint
). It allows us to have a perfect UX for the happy path following a canonical project layout, and reasonable UX for other situations (e.g., manually evoking qsc
on two files in different parts of your computer -- what is the "root"?).
@sezna should we update our samples to eliminate namespace declarations, since that's the recommended syntax now?
@sezna should we update our samples to eliminate namespace declarations, since that's the recommended syntax now?
Samples are tricky, because they are effectively "live" in that we point people to them online as reference. Given that, I think it's better to have the samples match features that are already shipped. That way we avoid the user trap of trying the samples and getting failures until we officially release a new feature.
@minestarks I was thinking we'd get all this in first, and then update samples etc. when documentation is ready for it. Since it is all backwards compatible, there's no rush.
A few questions to the PR description.
"We detect the "root" of the project by calculating the largest common prefix of the file paths."
- What if we are compiling just one file and have no explicit project?
- What if we have a project, but ALL files happen to be in "src/they/are/here"?
"manually evoking qsc on two files in different parts of your computer -- what is the "root"?"
- What if we invoke qsc on two files that are on different drives/shares?
- If we use a file
qs/x/y/z/a.qs
in two qsc invocations - withqs/x/y/z1/b1.qs
andqs/x/y2/b2.qs
, the namespace assumed for a.qs will be different?
A few questions to the PR description.
"We detect the "root" of the project by calculating the largest common prefix of the file paths."
- What if we are compiling just one file and have no explicit project?
In this scenario, the namespace doesn't matter much because you will never refer to items in the current file from another file, due to being in single-file mode. That being said, the project root is detected as the whole path (although once again, this doesn't matter): you can see that in this test case.
- What if we have a project, but ALL files happen to be in "src/they/are/here"?
This is the one shortcoming of this PR. Mine mentioned it here. The implementation required for the alternative approach was very fragile and not worth it. We jointly decided that it would be better to surface a warning or error if your src
directory does not contain any source files. I've filed #1516 to capture this work.
"manually evoking qsc on two files in different parts of your computer -- what is the "root"?"
- What if we invoke qsc on two files that are on different drives/shares?
qsc
is not a "shipped" binary so exotic scenarios for it are not our primary use case. That being said, you would either have to use explicit namespaces or make one a dependency of the other using the yet-to-be-released dependencies feature. That way they would be different compile units.
- If we use a file
qs/x/y/z/a.qs
in two qsc invocations - withqs/x/y/z1/b1.qs
andqs/x/y2/b2.qs
, the namespace assumed for a.qs will be different?
Yes. Ideally you would have your source files organized inside of a project and use our soon-to-be-released dependency management solution to be on the happy path.
Benchmark for 70d5d46
Click to view benchmark
Test | Base | PR | % |
---|---|---|---|
Array append evaluation | 337.6±21.61µs | 337.5±7.02µs | -0.03% |
Array literal evaluation | 189.2±1.00µs | 186.3±1.27µs | -1.53% |
Array update evaluation | 414.6±4.26µs | 417.9±24.28µs | +0.80% |
Core + Standard library compilation | 18.5±0.08ms | 19.1±0.60ms | +3.24% |
Deutsch-Jozsa evaluation | 5.1±0.32ms | 5.0±0.06ms | -1.96% |
Large file parity evaluation | 34.4±0.30ms | 34.7±0.51ms | +0.87% |
Large input file compilation | 12.4±0.66ms | 12.6±0.62ms | +1.61% |
Large input file compilation (interpreter) | 46.5±1.98ms | 46.2±0.96ms | -0.65% |
Large nested iteration | 32.8±0.17ms | 32.8±0.48ms | 0.00% |
Perform Runtime Capabilities Analysis (RCA) on Deutsch-Jozsa sample | 1549.7±33.84µs | 1564.8±39.33µs | +0.97% |
Perform Runtime Capabilities Analysis (RCA) on large file sample | 7.7±0.07ms | 7.7±0.18ms | 0.00% |
Perform Runtime Capabilities Analysis (RCA) on teleport sample | 1414.1±50.35µs | 1422.4±39.19µs | +0.59% |
Perform Runtime Capabilities Analysis (RCA) on the core and std libraries | 27.3±0.42ms | 27.6±1.50ms | +1.10% |
Teleport evaluation | 90.7±9.15µs | 87.2±3.52µs | -3.86% |