microsoft / QuantumLibraries

Q# libraries for the Quantum Development Kit

Home Page:https://docs.microsoft.com/quantum

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Consolidate Intrinsic and Canon under Microsoft.Quantum.Core

ausbin opened this issue · comments

Consolidate Intrinsic and Canon under Microsoft.Quantum.Core

Conceptual overview

The Q# compiler auto-opens namespace Microsoft.Quantum.Core, whereas iQ# (the Jupyter kernel for Q#) also auto-opens Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon. I believe the choice to open these automatically in iQ# was made before open namespaces were shared between notebook cells in iQ#, but this raises a good point:

opening Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon amounts to boilerplate required for almost any Q# program. Examples:

  1. All 32/32 of the solutions to the QuantumKatas open Microsoft.Quantum.Intrinsic, and 22/32 solutions open Microsoft.Quantum.Canon
  2. All of the project templates included in the VSCode extension open both these two namespaces
  3. Of the 115 Q# files in samples/ in the Microsoft/Quantum repository, 109 files open Microsoft.Quantum.Intrinsic, and 91 open Microsoft.Quantum.Canon

Proposal

Move everything from Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon into Microsoft.Quantum.Core.

Impact of breaking changes

Assuming the two namespaces still exist (albeit empty), programmers will not affected except for:

  1. Programmers who did not open either namespace, but defined their own e.g. H() operation in their own namespace. If they open their namespace and call their H(), they will get an AmbiguousCallable error
  2. Programmers using the fully qualified name to access something in Microsoft.Quantum.Intrinsic or Microsoft.Quantum.Canon
  3. Programmers opening Microsoft.Quantum.Intrinsic or Microsoft.Quantum.Canon with an alias

If my searching is failing and programmers are doing any of these 3 things, it's tough to think of a migration path. Leaving @Deprecated stubs in Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon that call into Microsoft.Quantum.Core may not work, since if someone opens Microsoft.Quantum.Intrinsic or Microsoft.Quantum.Canon, if they call anything from either of those namespaces, then they'll get a AmbiguousCallable error between the new callable and the stub.

One idea could be allowing attributes on namespaces so we can @Deprecate both Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon. Hopefully that could provide a more useful error message than the confusion programmers would see for (1)-(3) above, and it opens a path to eventually removing both namespaces.

Alternatives considered

An alternative is to have the compiler open Microsoft.Quantum.Intrinsic and Microsoft.Quantum.Canon by default. However, this is not as clean as always opening a single namespace: it seems like a slippery slope to continue adding more auto-opened namespaces to the compiler.

Thanks for opening this! To explain a bit of the thinking behind the split between Core, Intrinsic, and Canon, Core is the set of functions and operations needed to support Q# as a language at all — e.g.: Length is required in order for the language to make sense and to be coherent.

By contrast, one can imagine writing Q# programs that use a different QIS than that represented by Microsoft.Quantum.Intrinsic. Similarly, not all devices will support the combinator logic and similar utilities collected in Microsoft.Quantum.Canon. Thus, there's a real and material sense in which Microsoft.Quantum.Core is more "fundamental" than the other two, reflected in the auto-open behavior. Collapsing all of them into one namespace may be confusing as a result.

Definitely all for reducing boilerplate, though; we maybe could look to C#'s recent efforts (Q# is not C#, obviously, but we do learn from development in a variety of languages, including Python, Rust, and C#). In particular, when using .NET 6-style projects, a reasonable set of using statements is added by default, but that behavior can be turned off with a csproj property.

As another potential option, Rust's compact notation for opening namespaces may be helpful; e.g.: something like open Microsoft.Quantum.{Canon, Intrinsic};.

One other option may be to use F#-style @AutoOpen attributes. In F#, those can only be used from projects compiled as part of building the standard libraries, but it allows for decoupling auto-open decisions from the compiler implementation itself.

All by way of agreement, it would be good to reduce boilerplate — we may want to explore some other alternatives as to how to do so, though.

Rust, Haskell and other languages have a prelude, which is a module that is automatically imported in every file.

To explain a bit of the thinking behind the split between Core, Intrinsic, and Canon, Core is the set of functions and operations needed to support Q# as a language at all — e.g.: Length is required in order for the language to make sense and to be coherent.

Preludes usually re-export things defined in other modules (for example, see all the random things that std::prelude has). That's analogous to leaving things in Core, Intrinsic and Canon, but re-exporting them under either Core or a new prelude namespace.

By contrast, one can imagine writing Q# programs that use a different QIS than that represented by Microsoft.Quantum.Intrinsic. Similarly, not all devices will support the combinator logic and similar utilities collected in Microsoft.Quantum.Canon.

Preludes can be replaced. In Haskell, it's possible to turn off the default prelude and use another one that you like or more or that's more suited to a particular project. In Rust, it's common to see use foo::prelude::*; to get access to domain-specific utilities from a library. But I think it's important that there's one prelude module, rather than three, to make it easier on users.

I'm closing this in favor of preludes since (in my opinion) Cassandra is correct above