# C# Language Design Meeting for April 12th, 2021

## Agenda

1. [List patterns](#list-patterns)
2. [Lambda improvements](#lambda-improvements)

## Quote(s) of the Day

- "If the runtime team jumped off a bridge, would you [redacted]?" "If it would make my code faster"
- "Quote of the day may need to involve corn fields"

## Discussion

### List patterns

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

Today we looked over the changes around supporting `IEnumerable` types in list patterns, and how slice patterns will affect them. While
we overall like the lowering that has been proposed, we would like to continue to look at possible optimizations around using framework
apis such as `TryGetNonEnumeratedCount`. This will allow the framework to do all the grungy bookkeeping to make this quick for cases when
the `IEnumerable` is actually a countable type, or one of a certain type of LINQ expression that is countable under the hood (such as a
`Select` over an array). The runtime is also planning on adding a `Take` method that does similar bookkeeping with index and range, falling
back to buffering if it has to.

We also considered to what extent we should allow sub-patterns under a slice pattern. The obvious use case is a declaration pattern for the
slice, but as worded today the syntax will allow any arbitrary sub-pattern on the slice. After discussion, while we think that the main use
case is just declaration patterns, we see no reason to disallow other nested sub-patterns. It should compose well, and while 99% of users
will never need it, it could be helpful for that 1%.

The other question with slice patterns is whether to allow nested sub-patterns for `IEnumerable` as well as for sliceable types. If we do
allow this, it would mean treating `IEnumerable` specially here, while indexers and slicers cannot be used in the general case. Given
this, we think it will be a better experience if we only allow sub-patterns under a slice if the type is actually sliceable. Separately, we
can look at considering extension methods as a part of general sliceability, which, if combined with renaming that `Take` method from the
framework to `Slice` before it ships, would enable the feature in a much more general fashion.

#### Conclusions

* Lowering is generally approved.
* Slice sub-patterns will allow any pattern, so long as the input type to the list pattern is sliceable.
* Making `IEnumerable` sliceable is orthogonal.

### Lambda improvements

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md

After some implementation work, this proposal is back to look at some more changes. One of the big ones is moving the return type to the left
side of the parameter list. This makes the syntax analogous to our other signature declarations in C#, looking like an unnamed version of a
local function. This fits in well with the types of changes we've been making to C# in recent versions, such as tuples/record structs, anonymous
classes/record classes, and declaration forms/pattern forms. While we do have `Func` and `delegate*` as types that put the return last, we are
at least consistent within type forms and within signature declaration forms. At this point, the only parity we are missing between lambdas
and local functions is the ability to declare generic type parameters, but those won't be useful on lambda expressions unless we add
higher-kinded types to the language.

We also looked at requirements around return type specification: do we want to require that, if a return type is specified, parameter types must
be specified too? On the one hand, we require that, if any parameter types are specified, the others must be specified as well. On the other,
we can't make it required to specify the return type when a parameter type is specified because C# hasn't had such an ability until now, so
requiring this would lack symmetry. It's also very possible that the return type of a lambda could be uninferrable, while the parameter types
are, and it would be unfortunate if you had to specify everything in order to just get the return type. Given this, we will allow the return
type to be specified without parameter types.

We considered the case of `async async (async async) => async`. We could make it required to escape `async` used as a type name here: if the user
wants to specify the return type of the lambda is the type named `async` and the lambda is _not_ an async lambda, they'll have to escape it anyway
to disambiguate between making the lambda `async`. Given this, we support making it required to escape the type name in the return type.

The changes around attributes in the proposal raised no concerns. These changes were requiring that, if an attribute is used, parentheses must
be used for the parameter list, and the rules around how the compiler will disambiguate conditional array accesses and dictionary initializers.

Finally, we noted the possibility for a breaking change if single-method method groups are given a natural type with the following code:

```cs
using System;

var c = new C();
D.M(c.MyMethod);

// Outputs "Extension" today, would print Instance if c.MyMethod had a natural type.

class C
{
    public void MyMethod() { Console.WriteLine("Instance"); }
}

static class CExt
{
    public static void MyMethod(this C c, string s) { Console.WriteLine("Extension"); }
}

class D
{ 
    public static void M(Delegate d) { d.DynamicInvoke(); }
    public static void M(Action<string> a) { a(""); }
}
```

We'll need to investigate to see if we can avoid this change, or if we're ok with the potential break.

#### Conclusions

* Return type before the parameter list is approved, does not require parameter types to be specified.
* `async` as a return type name should require escapes.
* Attribute changes are approved.
