microsoft / qsharp-language

Official repository for design of the quantum programming language Q# and its core libraries
MIT License
235 stars 56 forks source link

Lambda expressions #12

Open bettinaheim opened 3 years ago

bettinaheim commented 3 years ago

Suggestion

The suggestion is to introduce a new kind of expression that constructs an anonymous callable. The expression would consist of an argument tuple or a symbol tuple, and a single expression that defines the value that is returned when the constructed callable is invoked. The constructed callable can be either an operation or a function, but cannot be type parametrized.

Considerations

The current mechanism of constructing anonymous callables locally is partial application. While rather powerful, it requires a declaration at a global scope based on which new callables can be constructed locally. The suggested lambda expressions would allow to avoid having to declare a callable specifically to construct a suitable anonymous callable via partial applications. Example 1 given below illustrates this based on an example from the standard libraries.

Alternatively, one could consider extending the support for declaring anonymous local callables to include callables with a body that consists of several statements. A dedicated syntax when the body of the anonymous callable merely consists of a single expression allows to define a more concise syntax for this case.

Context

Supporting to construct anonymous local callables with a body that rather than consisting of a single expression consists of a block of statements is to be considered as part of considering related features. A dedicated syntax for the suggested special case potentially introduces a redundant way to express the same functionality in the future. I believe the benefit of having a less verbose way of expressing what I expect to be a very common use case nonetheless merits that in this case.

The return type of the constructed callable can always be inferred and so can operation characteristics, if applicable. It may be possible to always infer the argument type as well, but one could also limit support for argument type inference and require that the argument type is explicitly specified (in certain cases). If the argument type is inferred, it is sufficient to define the name(s) of the argument (items) as a symbol tuple. The consequence of potentially introducing some form of global type inference in the future also need to be considered when discussing inference for lambda expressions.

Whether there are any restrictions on what the returned expression may contain (e.g. mutable variables) as well as when subexpressions are evaluated (i.e. whether they are captured) needs to be clarified.

Examples

Example 1:
The function WithFirstInputApplied is declared purely to construct a suitable function to return in CurriedOp via partial application. This additional global declaration becomes unnecessary if lambda expressions are available.

    internal function WithFirstInputApplied<'T, 'U> (op : (('T, 'U) => Unit), arg1 : 'T) : ('U => Unit) {
        return op(arg1, _);
    }

    function CurriedOp<'T, 'U> (op : (('T, 'U) => Unit)) : ('T -> ('U => Unit)) {
        return WithFirstInputApplied(op, _);
    }

Example 2:
Tentatively suggested syntax for lambda expressions:

/// Returns a function that adds i to a given value.
function Increment (i : Int) : (Int -> Int) {

    // unused statement to illustrate syntax
    let square = (a : Int) -> a*a; // the argument type is explicitly specified

    return a -> a + i; // the argument type can be inferred in this case
}

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

Please tick all that apply: