Precedence and associativity

Precedence and associativity define the order in which operators are applied. Operators with higher precedence are bound to their arguments (operands) first, while operators with the same precedence bind in the direction of their associativity. For example, the expression 1+2*3 according to the precedence for addition and multiplication is equivalent to 1+(2*3), and 2^3^4 equals 2^(3^4) since exponentiation is right-associative.

Operators

The following table lists the available operators in Q#, as well as their precedence and associativity. Additional modifiers and combinators are also listed, and bind tighter than any of these operators.

Description Syntax Operator Associativity Precedence
copy-and-update operator w/ <- ternary left 1
range operator .. infix left 2
conditional operator ? \| ternary right 3
logical OR or infix left 4
logical AND and infix left 5
bitwise OR \|\|\| infix left 6
bitwise XOR ^^^ infix left 7
bitwise AND &&& infix left 8
equality == infix left 9
inequality != infix left 9
less-than-or-equal <= infix left 10
less-than < infix left 11
greater-than-or-equal >= infix left 11
greater-than > infix left 11
right shift >>> infix left 12
left shift <<< infix left 12
addition or concatenation + infix left 13
subtraction - infix left 13
multiplication * infix left 14
division / infix left 14
modulus % infix left 14
exponentiation ^ infix right 15
bitwise NOT ~~~ prefix right 16
logical NOT not prefix right 16
negative - prefix right 16

Copy-and-update expressions necessarily need to have the lowest precedence to ensure a consistent behavior of the corresponding evaluate-and-reassign statement. Similar considerations hold for the range operator to ensure a consistent behavior of the corresponding contextual expression.

Modifiers and combinators

Modifiers can be seen as special operators that can be applied to certain expressions only. They can be assigned an artificial precedence to capture their behavior.

For more information, see Expressions.

This artificial precedence is listed in the following table, along with how the precedence of operators and modifiers relates to how tightly item access combinators ([,] and :: respectively) and call combinators ((, )) bind.

Description Syntax Operator Associativity Precedence
Call combinator ( ) n/a left 17
Adjoint functor Adjoint prefix right 18
Controlled functor Controlled prefix right 18
Unwrap application ! postfix left 19
Named item access :: n/a left 20
Array item access [ ] n/a left 20
Function lambda -> n/a right 21
Operation lambda => n/a right 21

To illustrate the implications of the assigned precedences, suppose you have a unitary operation DoNothing (as defined in Specialization declarations), a callable GetStatePrep that returns a unitary operation, and an array algorithms that contains items of type Algorithm defined as follows

    newtype Algorithm = (
        Register : Qubit[],
        Initialize : Transformation,
        Apply : Transformation
    );

    newtype Transformation =
        Qubit[] => Unit is Adj + Ctl;

The following expressions, then, are all valid:

    GetStatePrep()(arg)
    (Transformation(GetStatePrep()))!(arg)
    Adjoint DoNothing()
    Controlled Adjoint DoNothing(cs, ())
    Controlled algorithms[0]::Apply!(cs, _)
    algorithms[0]::Register![i]

Looking at the precedences defined in the table above, you can see that the parentheses around (Transformation(GetStatePrep())) are necessary for the subsequent unwrap operator to be applied to the Transformation value rather than the returned operation. However, parentheses are not required in GetStatePrep()(arg); functions are applied left-to-right, so this expression is equivalent to (GetStatePrep())(arg). Functor applications also don't require parentheses around them in order to invoke the corresponding specialization, nor do array or named item access expressions. Thus, the expression arr2D[i][j] is perfectly valid, as is algorithms[0]::Register![i].