Chiusure
Le chiusura sono chiamabili che acquisiscono variabili dall'ambiente contenitore. È possibile creare sia le chiusura di funzione che di operazione. Una chiusura dell'operazione può essere creata all'interno di una funzione, ma può essere applicata solo in un'operazione.
Q# include due meccanismi per la creazione di chiusura: espressioni lambda e applicazione parziale.
Espressioni lambda
Un'espressione lambda crea una funzione o un'operazione anonima.
La sintassi di base è una tupla di simboli per associare i parametri, una freccia (->
per una funzione e =>
per un'operazione) e un'espressione da valutare quando applicata.
// Function that captures 'x':
y -> x + y
// Operation that captures 'qubit':
deg => Rx(deg * PI() / 180.0, qubit)
// Function that captures nothing:
(x, y) -> x + y
Parametri
I parametri vengono associati usando una tupla di simboli identica al lato sinistro di un'istruzione di dichiarazione di variabile. Il tipo della tupla del parametro è implicito. Le annotazioni dei tipi non sono supportate; se l'inferenza del tipo ha esito negativo, potrebbe essere necessario creare una dichiarazione chiamabile di primo livello e usare invece un'applicazione parziale.
Variabili di acquisizione modificabili
Non è possibile acquisire variabili modificabili. Se è sufficiente acquisire il valore di una variabile modificabile all'istante in cui viene creata l'espressione lambda, è possibile creare una copia non modificabile:
// ERROR: 'variable' cannot be captured.
mutable variable = 1;
let f = () -> variable;
// OK.
let value = variable;
let g = () -> value;
Caratteristiche
Le caratteristiche di un'operazione anonima vengono dedotte in base alle applicazioni dell'espressione lambda. Se l'espressione lambda viene usata con un'applicazione functor o in un contesto che prevede una caratteristica, l'espressione lambda viene quindi dedotta per avere tale caratteristica. Ad esempio:
operation NoOp(q : Qubit) : Unit is Adj {}
operation Main() : Unit {
use q = Qubit();
let foo = () => NoOp(q);
foo(); // Has type Unit => Unit with no characteristics
let bar = () => NoOp(q);
Adjoint bar(); // Has type Unit => Unit is Adj
}
Se sono necessarie caratteristiche diverse per un'operazione lambda rispetto a quella dedotta, sarà invece necessario creare una dichiarazione di operazione di primo livello.
Applicazione parziale
L'applicazione parziale è una pratica abbreviata per l'applicazione di alcuni argomenti, ma non tutti, di argomenti chiamabili.
La sintassi è la stessa di un'espressione di chiamata, ma gli argomenti non validi vengono sostituiti con _
.
Concettualmente, l'applicazione parziale equivale a un'espressione lambda che acquisisce gli argomenti applicati e accetta gli argomenti non applicati come parametri.
Ad esempio, dato che f
è una funzione ed o
è un'operazione e la variabile x
acquisita non è modificabile:
Applicazione parziale | Espressioni lambda |
---|---|
f(x, _) |
a -> f(x, a) |
o(x, _) |
a => o(x, a) |
f(_, (1, _)) |
(a, b) -> f(a, (1, b)) [^1] |
f((_, _, x), (1, _)) |
((a, b), c) -> f((a, b, x), (1, c)) |
Variabili di acquisizione modificabili
A differenza delle espressioni lambda, l'applicazione parziale può acquisire automaticamente una copia del valore di una variabile modificabile:
mutable variable = 1;
let f = Foo(variable, _);
Equivale all'espressione lambda seguente:
mutable variable = 1;
let value = variable;
let f = x -> Foo(value, x);
[^1]: la tupla dei parametri è strettamente scritta (a, (b))
, ma (b)
equivale a b
.