Операции и функции

Как подробнее рассказывается в описании типов данных кубит, квантовые вычисления основаны на побочных эффектах операций, которые поддерживает целевой квантовый процессор. По сути, это единственные побочные эффекты в Q#. Так как все типы являются неизменяемыми, невозможно получить побочные эффекты, влияющие на явным образом представленное в Q# значение. Поэтому если реализация определенных вызываемых объектов не вызывает прямо или косвенно какие-либо из таких собственных операций, при ее выполнении всегда будут возвращаться одинаковые входные данные (при одинаковых входных).

Q# позволяет явным образом разбить такие детерминированные вычисления на функции. Так как набор собственных поддерживаемых инструкций не является строго фиксированным и встроенным в язык программирования, а настраивается для каждой системы отдельно в формате библиотеки Q#, детерминизм можно гарантировать только при том условии, что функция будет вызывать только другие функции, но не будет вызывать операции. Кроме того, собственные инструкции, не являющиеся детерминированными в силу своей квантовой природы, должны быть выражены как операции. При соблюдении этих двух ограничений функции могут вычислить результат сразу, когда будут известны входные значения, и их никогда не потребуется вычислять более одного раза для тех же входных данных.

По этим причинам Q# четко разделяет два типа вызываемых объектов: операции и функции. Все вызываемые объекты принимают в качестве входных данных единственный аргумент (который может содержать кортеж) и возвращают единственное значение (кортеж). Синтаксис для типа операций выражается как <TIn> => <TOut> is <Char>, где <TIn> нужно заменить типом аргумента, <TOut> — типом возвращаемого значения, а <Char> — характеристиками операции. Если характеристики операции указывать не нужно, используется упрощенный синтаксис: <TIn> => <TOut>. Аналогичным образом, типы функций выражаются так: <TIn> -> <TOut>.

Операции мало чем отличаются от функций, кроме описанного выше механизма гарантии детерминизма. И те, и другие являются значениями первого класса, которые можно свободно передавать в программах. И те, и другие можно использовать как возвращаемые значения или аргументы для других вызываемых объектов, как показано в примере ниже:

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
    return PowImpl(op, pow, _);
}

И те, и другие позволяют создавать экземпляры на основе параметризованного определения, как например представленная выше функция Powс параметризацией типа. И те, и другие можно применять частично, как в операторе return в примере.

Характеристики операций

В дополнение к информации о входных и выходных типах, тип операции содержит информацию о характеристиках этой операции. В частности, она определяет, какие функторы поддерживает эта операция. Кроме того, внутреннее представление содержит сведения для оптимизации, которые выводятся компилятором.

Характеристики операции задаются как набор предустановленных и (или) встроенных меток. Они выражаются в формате специальных выражений, входящих в состав сигнатуры типа. Такие выражения могут состоять из одного предустановленного набора меток или из некоторого сочетания характеристик, выраженных через поддерживаемый двоичный оператор.

Существуют два предопределенных набора: Adj и Ctl.

  • Набор Adj содержит одну метку, которая обозначает сопрягаемость операции, то есть поддержку функтора Adjoint и возможность "отменить" (то есть инвертировать) примененное квантовое преобразование.
  • Набор Ctl содержит одну метку, которая обозначает контролируемость операции, то есть поддержку функтора Controlled и возможность управлять ее выполнением через состояние других кубит.

В составе выражений характеристик поддерживаются два оператора: объединение наборов + и пересечение наборов *. В EBNF,

    predefined = "Adj" | "Ctl";
    characteristics = predefined 
        | "(", characteristics, ")" 
        | characteristics ("+"|"*") characteristics;

как и следует ожидать, * имеет более высокий приоритет, чем +, и оба они имеют левую ассоциативность. Тип унитарной операции выражается, например, в виде <TIn> => <TOut> is Adj + Ctl, где <TIn> следует заменить типом аргумента операции, а <TOut> — типом возвращаемого значения.

Примечание

Указание характеристик операции в таком формате имеет два важных преимущества. Во-первых, можно добавлять новые метки без экспоненциального роста количества ключевых слов языка, позволяющих выразить любое сочетание меток. И что еще важнее, использование выражений для обозначения характеристик операции позволит в будущем предоставить поддержку параметризации для характеристик операций.