# C# Language Design Meeting for March 3rd, 2021

## Agenda

1. [Natural type for lambdas](#natural-type-for-lambdas)
    1. [Attributes](#attributes)
    2. [Return types](#return-types)
    3. [Natural delegate types](#natural-delegate-types)
2. [Required members](#required-members)
3. [Appendix: ASP.NET examples](#appendix-aspnet-examples)

## Quote of the Day

- "I specifically avoided using the word to avoid settling on a pronunciation for everyone to get annoyed at."

## Discussion

### Natural type for lambdas

https://github.com/dotnet/csharplang/blob/master/proposals/lambda-improvements.md

Today we looked at a proposal around enhancing lambda expressions and method groups with a "natural type", which is helpful for several
ASP.NET scenarios. Overall, the LDM has general support for making enhancements here: explaining how lambdas and method groups don't have
natural types and why that means you can't use `var` or other similar scenarios has always been a pain point. Examples that show the
ASP.NET scenarios are at the bottom of these notes in [Appendix: ASP.NET examples](#appendix-aspnet-examples).

#### Attributes

There are some important details around how exactly these attributes are emitted to the final assembly that need to be ironed out, and this
likely will mean that we need to specify more about how lambdas themselves are emitted. This is not something we do today, so we need to make
sure that we are cautious enough about this specification that we don't box ourselves out of future lambda optimizations we can make today.

Syntax-wise, there's a couple of potential issues. We need to make sure that a leading `[Attribute]` is never ambiguous as an indexer on a
previous expression. If we require that the lambda is entirely parenthesized when an attribute is used it shouldn't be ambiguous, but
parenthesizing lambdas can be a pain and isn't the prettiest thing in the world. There's an additional question of whether we should always
require the arguments to a lambda to be parenthesized if there is a leading attribute. This code is visually ambiguous if unparenthesized,
for example:

```cs
[MyAttribute] x => x; // Does MyAttribute apply to the parameter or the entire lambda?
```

Another syntax question is around the requirement to specify the parameter type when attributing a parameter. We have an open proposal to
remove the requirement to specify the type when using `ref` or other parameter modifiers, so why add the requirement here? The main issue
is around making sure that we can parse the lambda. We added the requirement for modifiers previously because we weren't sure if we could
always parse the lambda. If we want to remove the requirement here as well, we need to make sure that we've done the work to ensure there
are no parsing issues.

#### Return types

Parsing issues abound here as well. `:` is _very_ dangerous from a parsing perspective, potentially requiring a large amount of lookahead and
recovery to figure out whether we're in a ternary or a lambda expression. Even if it does prove to always be unambiguous, we're not fans of
the amount of effort that disambiguation will require. Some potential alternatives we discussed:

```
() -> int { ... } // Potentially ambiguous with pointer derefs, but should be easier to disambiguate than the :
(-> int) { ... } // Also possibly ambiguous, but again easier to disambiguate than :
(return: int) { ... }
```

We could also look into forms that put the return type on the left side of the lambda, which fits more into existing method declaration
syntax today. However, lambdas are closer to `Func<T>` and function pointer types, and today both of those use the right-hand side for the
return type, so using a right-hand return type here would fit.

#### Natural delegate types

Our main potential concerns with introducing a natural type for lambdas and method groups comes from conversion scenarios. By introducing
a natural type and allowing it to convert to `Delegate` and making no other changes, it could potentially change resolution for `Expression<T>`
or other complicated inference scenarios. For most cases the more specific type in the better conversion would win, but we need to verify
that. A potential workaround is to define a specific type of conversion for delegate natural type to System.Delegate, and adjust better
conversion to always consider this conversion to be worse. We also should consider whether delegate natural types should be natively
convertible to `Expression<T>` as well.

Another important note is that by allowing single-method method groups to have a natural type, we'll be taking a step we explicitly avoided
in the function pointer feature from C# 9. By making single-method method groups have a type, we'll be introducing a new type of breaking
change to C#, as adding a new overload to a previously-not-overloaded will _always_ be a potential breaking change, even if no parameter
types are ambiguous.

Finally, we looked at whether we should avoid synthesizing delegate types, and just restrict natural types to those that can be expressed
as an `Action/Func`. While this would simplify implementation, it makes understanding the feature dramatically more complex, and prevents
using either ref parameters/returns or ref struct parameters/returns.

### Required members

https://github.com/dotnet/csharplang/issues/3630

Finally today, we took a broad look at another revision of the required members proposal, following up on feedback from the 
[last](LDM-2021-01-11.md#required-properties-simple-form) time it was presented to the LDM. The LDT overall likes the current form of the
proposal. There are still some open questions to resolve, but unlike previous iterations these are refinements to the proposal in its
current form, not total rewrites. Some questions raised today and recorded in the proposal are:

* What is the scope of the init clause? Does it introduce a new scope like the base clause does, or does it exist at the same scope as
the constructor? This affects whether it can use local functions defined in the constructor and whether the constructor body can shadow
locals.
* How many diagnostics do we want to have for silly scenarios, such as a required property that is never required from a constructor?
* What is the severity of diagnostics we are looking to have? Namely, warnings or errors?
* Are we using the right keywords?

### Appendix: ASP.NET examples

These are example ASP.NET programs we looked at when considering natural types, originally taken from https://github.com/halter73/HoudiniPlayground.

#### As written in C# 9

```cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

var app = WebApplication.Create(args);

[HttpGet("/")]
Todo GetTodo() => new(Id: 0, Name: "Play more!", IsComplete: false);
app.MapAction((Func<Todo>)GetTodo);

[HttpPost("/")]
Todo EchoTodo([FromBody] Todo todo) => todo;
app.MapAction((Func<Todo, Todo>)EchoTodo);

[HttpGet("/id/{id?}")]
IResult GetTodoFromId([FromRoute] int? id) =>
    id is null ?
    new StatusCodeResult(404) :
    new JsonResult(new Todo(Id: id.Value, Name: "From id!", IsComplete: false));
app.MapAction((Func<int?, IResult>)GetTodoFromId);

await app.RunAsync();

record Todo(int Id, string Name, bool IsComplete);
```

#### Simple proposal with natural types

```cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;

var app = WebApplication.Create(args);

app.MapAction([HttpGet("/")] () => new(Id: 0, Name: "Play more!", IsComplete: false));
app.MapAction([HttpPost("/")] ([FromBody] Todo todo) => todo);

await app.RunAsync();

record Todo(int Id, string Name, bool IsComplete);
```

#### A version that uses a lambda return type

```cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;

var app = WebApplication.Create(args);

app.MapAction([HttpPost("/")] ([FromBody] Todo todo) : Todo => todo);
app.MapAction([HttpGet("/")] () : Todo => new(Id: 0, Name: "Play more!", IsComplete: false));

app.MapAction([HttpGet("/id/{id?}")] ([FromRoute] int? id) : IResult => 
    id is null ?
    new StatusCodeResult(404) :
    new JsonResult(new Todo(Id: id.Value, Name: "From id!", IsComplete: false)));

await app.RunAsync();

record Todo(int Id, string Name, bool IsComplete);
```
