You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Lambda expressions (together with anonymous methods) are also called "anonymous functions". We are doing more and more to make them more similar to named local function declarations: Allowing the static modifier, attributes and explicit return types. One thing we do not yet allow is invoking them directly. For that they still need to be converted to a delegate type.
There are two main motivations to allow this:
As a way to enable statements and scopes in an expression context
To enhance the code environment; notably to make it async
Detailed design
From a grammar perspective direct invocation is already allowed, as long as the lambda expression is parenthesized:
For an anonymous function invocation the primary_expression shall be classified as an anonymous function. If the anonymous function is a lambda_expression with an implicit_anonymous_function_signature then the argument_list shall have a corresponding number of arguments and all of these arguments shall have types. These types are then taken to be the types of the corresponding parameters.
Considering the anonymous function to be a function member, it shall be applicable to the argument list.
As mentioned, anonymous function invocations can only parse if the anonymous function expression is parenthesized. While the spec doesn't seem to be entirely clear about this, parenthesized expressions can already be invoked today, and the kind of invocation hinges on the classification of the expression inside the parentheses. Essentially, except for parsing, parentheses are ignored.
This holds even when the parenthesized expression doesn't have a value; e.g. if it is a method group:
voidM(inti)=>WriteLine(i);M(1);// Normal method invocation(M)(2);// Disallowed only because of parsing ambiguity with cast expression((M))(3);// Allowed; parentheses are ignored
We should probably consider making the spec clearer about this, but invocation of parenthesized expressions is nothing new, and is not part of this proposal.
A note on implementation
A directly invoked anonymous function is very much like a local function. We should consider some of the same implementation strategies, e.g. passing as an extra ref argument a struct representing the closure.
Drawbacks
Direct invocation of lambda expressions, even though common in other languages, does not have great readability.
Alternatives
Natural delegate types
One alternative is for this to fall out of the "natural types for lambda expressions" feature. Essentially when a lambda expression has a natural delegate type, we would create the delegate and immediately invoke it.
The downsides are that it would not apply to lambdas without explicit parameter types, and that the semantics require a delegate to be created only to be immediately discarded (though this can likely be optimized away).
Expression blocks
The first scenario - fitting statements into an expression context - could be addressed by a dedicated feature, such as expression blocks. This would require new syntax, and has been the subject of some controversy. It would not address the second scenario; the need to create an async context for code.
Directly invoked anonymous functions
Summary
Allow (parenthesized) lambda expressions and anonymous methods to be directly invoked with an argument list:
Motivation
Lambda expressions (together with anonymous methods) are also called "anonymous functions". We are doing more and more to make them more similar to named local function declarations: Allowing the
static
modifier, attributes and explicit return types. One thing we do not yet allow is invoking them directly. For that they still need to be converted to a delegate type.There are two main motivations to allow this:
Detailed design
From a grammar perspective direct invocation is already allowed, as long as the lambda expression is parenthesized:
There are currently two kinds of invocation expressions: Method invocations (including extension method invocations) and delegate invocations. This proposal adds "anonymous function invocations" as follows:
For an anonymous function invocation the
primary_expression
shall be classified as an anonymous function. If the anonymous function is alambda_expression
with animplicit_anonymous_function_signature
then theargument_list
shall have a corresponding number of arguments and all of these arguments shall have types. These types are then taken to be the types of the corresponding parameters.Considering the anonymous function to be a function member, it shall be applicable to the argument list.
At runtime an anonymous function invocation is processed as a function member invocation.
A note on parentheses
As mentioned, anonymous function invocations can only parse if the anonymous function expression is parenthesized. While the spec doesn't seem to be entirely clear about this, parenthesized expressions can already be invoked today, and the kind of invocation hinges on the classification of the expression inside the parentheses. Essentially, except for parsing, parentheses are ignored.
This holds even when the parenthesized expression doesn't have a value; e.g. if it is a method group:
We should probably consider making the spec clearer about this, but invocation of parenthesized expressions is nothing new, and is not part of this proposal.
A note on implementation
A directly invoked anonymous function is very much like a local function. We should consider some of the same implementation strategies, e.g. passing as an extra ref argument a struct representing the closure.
Drawbacks
Direct invocation of lambda expressions, even though common in other languages, does not have great readability.
Alternatives
Natural delegate types
One alternative is for this to fall out of the "natural types for lambda expressions" feature. Essentially when a lambda expression has a natural delegate type, we would create the delegate and immediately invoke it.
The downsides are that it would not apply to lambdas without explicit parameter types, and that the semantics require a delegate to be created only to be immediately discarded (though this can likely be optimized away).
Expression blocks
The first scenario - fitting statements into an expression context - could be addressed by a dedicated feature, such as expression blocks. This would require new syntax, and has been the subject of some controversy. It would not address the second scenario; the need to create an async context for code.
Unresolved questions
No known unknowns.
Design meetings
Direct invocation of lambda expressions was discussed in C# LDM on May 10, 2021.
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-28.md#ungrouped
The text was updated successfully, but these errors were encountered: