From 1b8f43e2ccf07393debfbfef70bc7c7104761a39 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 29 Mar 2022 15:47:40 -0700 Subject: [PATCH 1/4] Document lambda expressions --- .../Language/2_Statements/CallStatements.md | 2 +- .../Language/3_Expressions/Closures.md | 118 ++++++++++++++++++ .../3_Expressions/PartialApplication.md | 14 --- .../Language/3_Expressions/ValueLiterals.md | 9 +- .../4_TypeSystem/OperationsAndFunctions.md | 2 +- Specifications/Language/README.md | 2 +- 6 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 Specifications/Language/3_Expressions/Closures.md delete mode 100644 Specifications/Language/3_Expressions/PartialApplication.md diff --git a/Specifications/Language/2_Statements/CallStatements.md b/Specifications/Language/2_Statements/CallStatements.md index 1428d1c6..9e87bc7c 100644 --- a/Specifications/Language/2_Statements/CallStatements.md +++ b/Specifications/Language/2_Statements/CallStatements.md @@ -1,6 +1,6 @@ # Call Statements -Call statements are a very important part of any programming language. While operation and function calls, much like [partial applications](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PartialApplication.md#partial-application), can be used as an expression anywhere as long as the returned value is of a suitable type, they can also be used as statements if they return `Unit`. +Call statements are a very important part of any programming language. While operation and function calls can be used as an expression anywhere as long as the returned value is of a suitable type, they can also be used as statements if they return `Unit`. The usefulness of calling functions in this form primarily lays in debugging, whereas such operation calls are one of the most common constructs in any Q# program. At the same time, operations can only be called from within other operations and not from within functions (for context, see also [this section](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/4_TypeSystem/QuantumDataTypes.md#qubits)). With callables being first-class values, diff --git a/Specifications/Language/3_Expressions/Closures.md b/Specifications/Language/3_Expressions/Closures.md new file mode 100644 index 00000000..06b18c5e --- /dev/null +++ b/Specifications/Language/3_Expressions/Closures.md @@ -0,0 +1,118 @@ +# Closures + +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. + +## Lambda Expressions + +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. + +```qsharp +// 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 + +Parameters are bound using a symbol tuple that is identical to the left-hand side of a [variable declaration statement](../2_Statements/VariableDeclarationsAndReassignments.md). +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: + +```qsharp +// ERROR: 'variable' cannot be captured. +mutable variable = 1; +let f = () -> variable; + +// OK. +let value = variable; +let g = () -> value; +``` + +### Characteristics + +The characteristics of an anonymous operation are inferred based on the body of the lambda expression. +For example: + +```qsharp +// 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 by the point where the lambda expression occurs. +For example: + +```qsharp +let foo = op => op(q); +foo(X); +``` + +Here, `foo` is inferred to have type: + +```qsharp +(Qubit => Unit is Adj + Ctl) => Unit +``` + +But that type is *not* the principal type for `foo`, which is: + +```qsharp +(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 + +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 | +| ---------------------- | ------------------------------------- | +| `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))` | + +[^1]: The parameter tuple is strictly written `(a, (b))`, but [`(b)` is equivalent to `b`](../4_TypeSystem/SingletonTupleEquivalence.md). + +### Mutable Capture Variables + +Unlike lambda expressions, partial application can automatically capture a copy of the value of a mutable variable: + +```qsharp +mutable variable = 1; +let f = Foo(variable, _); +``` + +This is equivalent to the following lambda expression: + +```qsharp +mutable variable = 1; +let value = variable; +let f = x -> Foo(value, x); +``` + +--- + +← [Back to Index](https://github.com/microsoft/qsharp-language/tree/main/Specifications/Language#index) diff --git a/Specifications/Language/3_Expressions/PartialApplication.md b/Specifications/Language/3_Expressions/PartialApplication.md deleted file mode 100644 index 2a903cd4..00000000 --- a/Specifications/Language/3_Expressions/PartialApplication.md +++ /dev/null @@ -1,14 +0,0 @@ -# Partial Application - -Callables are declared at a global scope and by default can be used anywhere in the same project and in a project that references the assembly in which they are declared. However, often there is a need to construct a callable for use in a local context only. Q# currently provides one rather powerful mechanism to construct new callables on the fly: partial applications. - -Partial application refers to that some of the argument items to a callable are provided while others are still missing as indicated by an underscore. The result is a new callable value that takes the remaining argument items, combines them with the already given ones, and invokes the original callable. Naturally, partial application preserves the characteristics of a callable, i.e. a callable constructed by partial application supports the same functors as the original callable. - -Q# allows any subset of the parameters to be left unspecified, not just a final sequence, which ties in more naturally with the design to have each callable take and return exactly one value. -For a function `Foo` whose argument type is `(Int, (Double, Bool), Int)` for instance, `Foo(_, (1.0, _), 1)` is a function that takes an argument of type `(Int, (Bool))`, which is the same as an argument of type `(Int, Bool)`, see [this section](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/4_TypeSystem/SingletonTupleEquivalence.md#singleton-tuple-equivalence). - -Because partial application of an operation does not actually evaluate the operation, it has -no impact on the quantum state. This means that building a new operation from existing operations and computed data may be done in a function; this is useful in many adaptive quantum algorithms and in defining new control flow constructs. - - -← [Back to Index](https://github.com/microsoft/qsharp-language/tree/main/Specifications/Language#index) \ No newline at end of file diff --git a/Specifications/Language/3_Expressions/ValueLiterals.md b/Specifications/Language/3_Expressions/ValueLiterals.md index ac638d32..a485087e 100644 --- a/Specifications/Language/3_Expressions/ValueLiterals.md +++ b/Specifications/Language/3_Expressions/ValueLiterals.md @@ -138,14 +138,9 @@ Values of a [user defined type](https://github.com/microsoft/qsharp-language/tre For instance, if `IntPair` has two items of type `Int`, then `IntPair(2, 3)` creates a new instance by invoking the default constructor. -## Operation Literals - -No literals exist for values of [operation type](https://github.com/microsoft/qsharp-language/tree/main/Specifications/Language/4_TypeSystem#available-types); operations have to be [declared](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/1_ProgramStructure/3_CallableDeclarations.md#callable-declarations) on a global scope and new operations can be constructed locally using [partial application](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PartialApplication.md#partial-application). - -## Function Literals - -No literals exist for values of [function type](https://github.com/microsoft/qsharp-language/tree/main/Specifications/Language/4_TypeSystem#available-types); functions have to be [declared](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/1_ProgramStructure/3_CallableDeclarations.md#callable-declarations) on a global scope and new functions can be constructed locally using [partial application](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PartialApplication.md#partial-application). +## Operation and Function Literals +Anonymous operations and functions can be created using a [lambda expression](Closures.md#lambda-expressions). ## Default Values diff --git a/Specifications/Language/4_TypeSystem/OperationsAndFunctions.md b/Specifications/Language/4_TypeSystem/OperationsAndFunctions.md index 9be2dcca..392ce7a1 100644 --- a/Specifications/Language/4_TypeSystem/OperationsAndFunctions.md +++ b/Specifications/Language/4_TypeSystem/OperationsAndFunctions.md @@ -13,7 +13,7 @@ function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit { } ``` -They can be instantiated based on a type parametrized definition such as, e.g., the [type parametrized](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/4_TypeSystem/TypeParameterizations.md#type-parameterizations) function `Pow` above, and they can be [partially applied](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PartialApplication.md#partial-application) as done in Line 2 in the example. +They can be instantiated based on a type parametrized definition such as, e.g., the [type parametrized](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/4_TypeSystem/TypeParameterizations.md#type-parameterizations) function `Pow` above, and they can be [partially applied](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/Closures.md#partial-application) as done in Line 2 in the example. ## Operation Characteristics diff --git a/Specifications/Language/README.md b/Specifications/Language/README.md index ac7e73a0..88729149 100644 --- a/Specifications/Language/README.md +++ b/Specifications/Language/README.md @@ -45,7 +45,7 @@ Q# implements programs in terms of statements and expressions, much like classic 1. [Arithmetic Expressions](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/ArithmeticExpressions.md#arithmetic-expressions) 1. [Concatenations](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/Concatentation.md#concatenation) 1. [Modifiers \& Combinators](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PrecedenceAndAssociativity.md#modifiers-and-combinators) - 1. [Partial Application](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/PartialApplication.md#partial-application) + 1. [Closures](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/Closures.md) 1. [Functor Application](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/FunctorApplication.md#functor-application) 1. [Item Access Expressions](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/ItemAccessExpressions.md#item-access) 1. [Contextual Expressions](https://github.com/microsoft/qsharp-language/blob/main/Specifications/Language/3_Expressions/ContextualExpressions.md#contextual-and-omitted-expressions) From 3e5c1ba1af23e2a9603463381aadb05e02f38e11 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Tue, 29 Mar 2022 15:49:39 -0700 Subject: [PATCH 2/4] Move footnote to bottom --- Specifications/Language/3_Expressions/Closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specifications/Language/3_Expressions/Closures.md b/Specifications/Language/3_Expressions/Closures.md index 06b18c5e..019a6381 100644 --- a/Specifications/Language/3_Expressions/Closures.md +++ b/Specifications/Language/3_Expressions/Closures.md @@ -94,8 +94,6 @@ For example, given that `f` is a function and `o` is an operation, and the captu | `f(_, (1, _))` | `(a, b) -> f(a, (1, b))`[^1] | | `f((_, _, x), (1, _))` | `((a, b), c) -> f((a, b, x), (1, c))` | -[^1]: The parameter tuple is strictly written `(a, (b))`, but [`(b)` is equivalent to `b`](../4_TypeSystem/SingletonTupleEquivalence.md). - ### Mutable Capture Variables Unlike lambda expressions, partial application can automatically capture a copy of the value of a mutable variable: @@ -116,3 +114,5 @@ let f = x -> Foo(value, x); --- ← [Back to Index](https://github.com/microsoft/qsharp-language/tree/main/Specifications/Language#index) + +[^1]: The parameter tuple is strictly written `(a, (b))`, but [`(b)` is equivalent to `b`](../4_TypeSystem/SingletonTupleEquivalence.md). From 236193cf2c41022df711b4781a08f3ce466615b8 Mon Sep 17 00:00:00 2001 From: Sarah Marshall <33814365+samarsha@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:13:27 -0700 Subject: [PATCH 3/4] Update Specifications/Language/3_Expressions/Closures.md Co-authored-by: Alan Geller --- Specifications/Language/3_Expressions/Closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specifications/Language/3_Expressions/Closures.md b/Specifications/Language/3_Expressions/Closures.md index 019a6381..0bb6abeb 100644 --- a/Specifications/Language/3_Expressions/Closures.md +++ b/Specifications/Language/3_Expressions/Closures.md @@ -57,7 +57,7 @@ For example: () => M(q) ``` -Because of limitations in characteristics inference, this is based only on type information known by the point where the lambda expression occurs. +Because of limitations in characteristics inference, this is based only on type information known at the point where the lambda expression occurs. For example: ```qsharp From 2495c6c02edf05561621856b5eda1fab1e7638a3 Mon Sep 17 00:00:00 2001 From: Sarah Marshall Date: Thu, 7 Jul 2022 12:28:23 -0700 Subject: [PATCH 4/4] Improve wording in characteristics example --- Specifications/Language/3_Expressions/Closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specifications/Language/3_Expressions/Closures.md b/Specifications/Language/3_Expressions/Closures.md index 0bb6abeb..e171daf5 100644 --- a/Specifications/Language/3_Expressions/Closures.md +++ b/Specifications/Language/3_Expressions/Closures.md @@ -65,13 +65,13 @@ let foo = op => op(q); foo(X); ``` -Here, `foo` is inferred to have type: +`foo` is inferred to have the following type based on both the body of the lambda and the type of `X`: ```qsharp (Qubit => Unit is Adj + Ctl) => Unit ``` -But that type is *not* the principal type for `foo`, which is: +But the most specific type that `foo` could have is: ```qsharp (Qubit => Unit is Adj + Ctl) => Unit is Adj + Ctl