Closures are callables that capture variables from the enclosing environment. Both function and operation closures can be created. An operation closure can be created inside a function, but it can only be applied in an operation.
Q# has two mechanisms for creating closures: lambda expressions and partial application.
A lambda expression creates an anonymous function or operation.
The basic syntax is a symbol tuple to bind the parameters, an arrow (
-> for a function and
=> for an operation), and an expression to evaluate when applied.
// 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
Parameters are bound using a symbol tuple that is identical to the left-hand side of a variable declaration statement. The type of the parameter tuple is implicit. Type annotations are not supported; if type inference fails, you may need to create a top-level callable declaration and use partial application instead.
Mutable capture variables
Mutable variables cannot be captured. If you only need to capture the value of a mutable variable at the instant the lambda expression is created, you can create an immutable copy:
// ERROR: 'variable' cannot be captured. mutable variable = 1; let f = () -> variable; // OK. let value = variable; let g = () -> value;
The characteristics of an anonymous operation are inferred based on the body of the lambda expression. For example:
// Has type Unit => Unit is Adj + Ctl because X is known to be Adj + Ctl. () => X(q) // Has type Unit => Result because M is neither Adj nor Ctl. () => M(q)
Because of limitations in characteristics inference, this is based only on type information known at the point where the lambda expression occurs. For example:
let foo = op => op(q); foo(X);
foo is inferred to have the following type based on both the body of the lambda and the type of
(Qubit => Unit is Adj + Ctl) => Unit
But the most specific type that
foo could have is:
(Qubit => Unit is Adj + Ctl) => Unit is Adj + Ctl
If you need different characteristics for an operation lambda than what was inferred, you will need to create a top-level operation declaration instead.
Partial application is a convenient shorthand for applying some, but not all, of a callable's arguments.
The syntax is the same as a call expression, but unapplied arguments are replaced with
Conceptually, partial application is equivalent to a lambda expression that captures the applied arguments and takes in the unapplied arguments as parameters.
For example, given that
f is a function and
o is an operation, and the captured variable
x is immutable:
|Partial application||Lambda expression|
Mutable capture variables
Unlike lambda expressions, partial application can automatically capture a copy of the value of a mutable variable:
mutable variable = 1; let f = Foo(variable, _);
This is equivalent to the following lambda expression:
mutable variable = 1; let value = variable; let f = x -> Foo(value, x);
[^1]: The parameter tuple is strictly written
(a, (b)), but
(b) is equivalent to
Submit and view feedback for