Type Declarations

Q# supports user-defined types. User-defined types are similar to record types in F#; they are immutable but support a copy-and-update construct.

User-defined types

User-defined types may contain both named and anonymous items. The following declaration within a namespace, for example, defines a type Complex which has two named items Real and Imaginary, both of type Double:

    newtype Complex = (Real: Double, Imaginary : Double);

Any combination of named and unnamed items is supported, and inner items may also be named. For example, the type Nested, defined as

newtype Nested = (Double, (ItemName : Int, String)); 

contains two anonymous items of type Double and String respectively, and a named item ItemName of type Int.

You can access the contained items via their name or by deconstruction (for more information, see item access). You can also access a tuple of all items where the shape matches the one defined in the declaration via the unwrap operator.

User-defined types are useful for two reasons. First, as long as the libraries and programs that use the defined types access items via their name rather than by deconstruction, the type can be extended to contain additional items later on without breaking any library code. Because of this, accessing items via deconstruction is generally discouraged.

Second, Q# allows you to convey the intent and expectations for a specific data type since there is no automatic conversion between values of two user-defined types, even if their item types are identical.

For example, the arithmetic library includes quantum arithmetic operations for both big-endian and little-endian quantum integers. It hence defines two types, BigEndian and LittleEndian, both of which contain a single anonymous item of type Qubit[]:

    newtype BigEndian = Qubit[];
    newtype LittleEndian = Qubit[];

These types allow operations to specify whether they are written for big-endian or little-endian representations and leverages the type system to ensure at compile-time that mismatched operands aren't allowed.

Type names must be unique within a namespace and may not conflict with operation and function names. Types may not have circular dependencies in Q#; that is, defining something like a directly or indirectly recursive type is not allowed. For example, the following construct will give a compilation error:

    newtype Foo = (Foo, Int); // gives an error
    newtype Bar = Baz;        // gives an error
    newtype Baz = Bar;        // gives an error

User-defined constructors

Constructors for user-defined types are automatically generated by the compiler. Currently, it is not possible to define a custom constructor, though this may be an addition to the language in the future.