Copy-and-update expressions

To reduce the need for mutable bindings, Q# supports copy-and-update expressions for arrays, which allow you to access items via an index or range of indices.

Copy-and-update expressions instantiate a new value with all items set to the corresponding value in the original expression, except certain specified items, which are set to the ones defined on the right-hand side of the expression. They are constructed using a ternary operator w/ <-; the syntax w/ should be read as the commonly used short notation for "with":

    original w/ itemAccess <- modification

where original is either an expression of user-defined type or an array expression. For the corresponding requirements for itemAccess and modification, see Copy-and-update of struct types and Copy-and-update of arrays.

In terms of precedence, the copy-and-update operator is left-associative and has lowest precedence, and, in particular, lower precedence than the range operator (..) or the ternary conditional operator (? |). The chosen left associativity allows easy chaining of copy-and-update expressions:

    let model = Default<SequentialModel>()
        w/ Structure <- ClassifierStructure()
        w/ Parameters <- parameters
        w/ Bias <- bias;

As for any operator that constructs an expression of the same type as the left-most expression involved, the corresponding evaluate-and-reassign statement is available. The two following statements, for example, achieve the following: The first statement declares a mutable variable arr and binds it to the default value of an integer array. The second statement then builds a new array with the first item (with index 0) set to 10 and reassigns it to arr.

    mutable arr = [0, size = 3]; // arr contains [0, 0, 0]
    set arr w/= 0 <- 10;      // arr contains [10, 0, 0] 

The second statement is just short-hand for the more verbose syntax set arr = arr w/ 0 <- 10;.

Copy-and-update of arrays

For arrays, itemAccess can be an arbitrary expression of a suitable type; the same types that are valid for array slicing are valid in this context. Concretely, the itemAccess expression can be of type Int or Range. If itemAccess is a value of type Int, then the type of modification has to match the item type of the array. If itemAccess is a value of type Range, then the type of modification has to be the same as the array type.

For example, if arr contains an array [0, 1, 2, 3], then

  • arr w/ 0 <- 10 is the array [10, 1, 2, 3].
  • arr w/ 2 <- 10 is the array [0, 1, 10, 3].
  • arr w/ 0..2..3 <- [10, 12] is the array [10, 1, 12, 3].

Copy-and-update expressions allow the efficient creation of new arrays based on existing ones. The implementation for copy-and-update expressions avoids copying the entire array by duplicating only the necessary parts to achieve the desired behavior and performs an in-place modification if possible. Hence, array initializations do not incur additional overhead due to immutability.

The Microsoft.Quantum.Arrays namespace provides an arsenal of convenient tools for array creation and manipulation.

Copy-and-update expressions are a convenient way to construct new arrays on the fly; the following expression, for example, evaluates to an array with all items set to PauliI, except the item at index i, which is set to PauliZ:

[PauliI, size = n] w/ i <- PauliZ

Copy-and-update of struct types

To copy and update struct types, you use a copy constructor to declare a new struct from an existing one. For example, if MyPair is an instance of the struct IntPair, with the values 5 and 7, you can create a new struct with the same values using the new keyword and the name of the existing struct. For more information on defining a struct type, see Type declarations.

let ThisPair = new IntPair { ...MyPair };

Or you can modify one or more of the values when you create it

let ThisPair = new IntPair { ...MyPair, Int1 = 8, Int2 = 10 };