Chapter 1. Program Structure

This chapter offers an overview of Q# program structure and the main elements it comprises. This will help you orient yourself in the following chapters, in which we’ll dive deeper into writing Q# code.

Your First Q# Program

Example 1-1 shows a Q# program that prints a message to the console. While very simple, it shows the main elements of Q# code: a namespace, an open directive, an operation that contains the actual Q# statements, and comments.

Note

The code samples in Part I of this book are written as Q# standalone applications. You can follow the installation instructions to set up the Microsoft Quantum Development Kit for your environment and run these samples. In Chapter 6, we’ll discuss different ways to run Q# programs and the nuances of writing Q# code for each of them.

Example 1-1. “Hello World” program in Q#
namespace HelloQuantumWorld {
    open Microsoft.Quantum.Intrinsic; // To use Message

    /// # Summary
    /// Prints a message to the console.
    @EntryPoint()
    operation SayHello() : Unit {
        Message("Hello quantum world!");
    }
}
Note

Once you’ve installed the QDK for your preferred development environment, you can create a Q# project, copy-paste Example 1-1 to the .qs file in the project, and run it following these instructions. You should see a console window with this text message in it:

Hello quantum world!

Congratulations, you just ran your first Q# program!

Let’s take a closer look at the code elements that are shown in Example 1-1.

Namespaces

At the outermost level, all Q# code is organized into namespaces. A namespace is a collection of uniquely named operations, functions (collectively referred to as callables), and user-defined types.

The namespace keyword defines a namespace for the Q# code within the following block:

namespace HelloQuantumWorld {
    // Operations, functions, and user-defined
    // types within this block will belong to
    // the namespace HelloQuantumWorld.
}

The namespace name can contain letters, digits, underscores, and dots.

Unlike .NET languages, Q# doesn’t have a hierarchy of nested namespaces defined by the dots in their names. Namespaces cannot be nested inside each other. Namespaces with names that contain each other can be related logically, but this relation will not be reflected on the code level.

For example, Q# libraries offer the logically connected namespaces Microsoft.Quantum.MachineLearning and Microsoft.Quantum.MachineLearning.Datasets, but the operations defined in the latter are defined independently from the former.

All Q# code, except comments, must be included in a namespace. (Q# code snippets written in Jupyter Notebooks may seem to be an exception from this rule, as they cannot be defined inside a namespace. However, internally, Jupyter Notebooks paste the contents of Q# code cells into a temporary namespace before compiling them. We will discuss the nuances of writing Q# code in Jupyter Notebooks in more detail in Chapter 6.)

The code within a namespace can access any other code declared in the same namespace without extra qualification. To access a callable or a user-defined type declared in a different namespace, you have to use either its fully qualified name or an open directive.

A fully qualified name of a callable or a type is its namespace name, followed by a dot and its unqualified name:

// This uses the fully qualified operation name.
Microsoft.Quantum.Intrinsic.Message("Hello!");

An open directive imports all callables and types from that namespace and makes them available as if they were defined in the current namespace. Open directives must be the first element within the namespace:

// Open the namespace which defines Message.
open Microsoft.Quantum.Intrinsic;

// Use unqualified name of the operation.
Message("Hello!");

Note that a callable definition in the current namespace has precedence over any definitions of callables with the same name in other open namespaces. Two callables with the same name coming from different open namespaces, other than the current namespace, don’t have precedence over each other, so you have to use the fully qualified name to specify which one you want to call.

Open directives also allow you to define namespace aliases, which you can use instead of full namespace names when qualifying callables or types from those namespaces. This can be useful, for example, to disambiguate if operations with the same name are defined in two open namespaces. Defining an alias for a namespace doesn’t open it:

// Create an alias for the namespace which
// defines Message (without opening it).
open Microsoft.Quantum.Intrinsic as Builtin;

// Use the alias with the operation name.
Builtin.Message("Hello!");

Operations and Functions

The main element of namespaces that holds most of the Q# code is operations and functions—series of statements that perform certain actions. Operations are general-purpose subroutines—sequences of instructions that perform a certain task, packaged as a unit. Functions are a special type of subroutines that perform only deterministic classical computations. By contrast, operations have no restrictions on the actions they perform, and they may or may not involve quantum computations.

An operation declaration will use the keyword operation, followed by the operation name, the list of its input parameters, and the type of its return:

operation SayHello() : Unit {
    Message("Hello quantum world!");
}

In this example, we define an operation called SayHello, which takes no parameters and has no return, as denoted by the Unit return type. (I’ll talk more about the uses of Unit in Chapter 2.) The only thing it does is print the message to the console.

Including the @EntryPoint() attribute before the operation SayHello marks the operation that will be called first when the Q# standalone application is executed (more on that in Chapter 6).

Functions are defined similarly, using the keyword function instead of operation.

Operations and functions are a fundamental concept in Q#, and there is a lot more to defining them than has been mentioned here. We will discuss defining operations and functions in more detail in Chapter 5. Meanwhile, this summary should help you read the basic code samples in the following chapters.

Type Declarations

Another namespace element is type declarations—declarations of user-defined types. User-defined types are tuples of named and anonymous items of other types (primitive or user-defined).

The newtype keyword declares a user-defined type:

// Declare type for pair.
newtype Pair = (First : Int, Second : Int);

We will discuss declaring user-defined types in more detail in Chapter 2.

Comments

Q# offers two types of comments—implementation comments (more often referred to as single-line comments) and documentation comments.

Implementation comments allow you to include notes on the Q# code in the code itself. They are intended for other developers working on the same code—your teammates or even your future self—and should offer additional context that might not be clear from the code itself: the design decisions made during code development, links to resources describing the techniques used, etc.

A single-line comment begins with two forward slashes (//) and ends at the end of the line:

// This is a single-line comment.
let a = 1;   // Can start mid-line.

Q# doesn’t currently support multiline (block) comments.

Documentation comments allow you to embed the external documentation for your Q# code into the code itself. Their audience is the users of your code, and they should describe the purpose of the code and the meaning and the format of the input parameters, offer usage examples, and so forth.

A documentation comment begins with three forward slashes (///) and ends at the end of the line. Documentation comments appear in blocks preceding operations, functions, or type declarations:

/// # Summary
/// Prints a message to the console.
operation SayHello() : Unit {
    Message("Hello quantum world!");
}

The Q# compiler parses documentation comments to provide quick info in IDEs that offer IntelliSense support for Q#, such as Visual Studio and Visual Studio Code. Figure 1-1 shows an example of the quick info window in VS Code.

Quick info window in Visual Studio Code
Figure 1-1. Quick info window in Visual Studio Code

Additionally, the Q# documentation generator uses documentation comments in Q# libraries’ source code to generate the API reference.

You can use Markdown language in the documentation comments you write to enable formatting and cross-references in the generated API documentation, and LaTeX to support math formulas. The Q# documentation page on comments provides more information on the structure of documentation comments.

Conclusion

Now that you’re familiar with the high-level structure of Q# programs and running them, you are ready to dive deeper into the Q# programming language. In the next chapter, you will learn about the data types offered by Q# and learn to create your own user-defined types.

Get Q# Pocket Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.