# C# Language Design Meeting for June 14th, 2021

## Agenda

1. [Open questions in CallerArgumentExpressionAttribute](#open-questions-in-CallerArgumentExpressionAttribute)
2. [List pattern syntax](#list-pattern-syntax)

## Quote of the Day

- "My first reaction is that I thought we got rid of the C# test team for testing esoteric scenarios"

## Discussion

### Open questions in CallerArgumentExpressionAttribute

https://github.com/dotnet/roslyn/issues/52745#issuecomment-849961999  
https://github.com/dotnet/csharplang/issues/287

#### VB Support

Conclusion: yes, we should support VB here.

#### Generated code

This question centers around this example:

```cs
void M([CallerMemberName]string arg1 = "1", [CallerArgumentExpression("arg1")]string arg2 = "2")
{
    Console.WriteLine(arg2); // What gets printed?
}

void M2()
{
    M();
}
```

As we see it, there are 5 possible values that a reasonable programmer could expect.

1. `null`
2. The empty string: `""`.
3. The default value of `arg1`, as an expression: `"\"1\""`.
4. The default value of `arg2`: `"2"`.
5. The value filled in for `arg1`, as an expression: `"\"M2\""`.

We don't think option 1 is useful here, as the parameter is attributed to not accept `null`, and this
would just mean that every use of `CallerArgumentExpression` would be required to handle the `null` case.
We also don't think that options 3 or 5 are really correct either: the attribute here is about providing
the specific syntax the user used, not the _value_ the user used. There are many ways to express the values
given as a constant value: we could just turn `"M2"` into a string, or we could say `"\"" + "M" + "2" + "\""`.
Both are technically correct, but neither reflects what the user actually wrote. Finally, for option 3, we
think that this is trying to second-guess the user. They provided a default value for the parameter, and if
we never respect that value then the default value was useless. Given these, we think the correct approach
is option 4.

##### Conclusion

Option 4: the default value of the parameter will be used. We will not turn compiler-generated code into
equivalent C# expressions.

#### Self-referential arguments

Consider these examples:

```cs
void M3([CallerArgumentExpression("arg1")]string arg1 = ""); // Warning?

M3(); // What gets passed? null? ""?
```

##### Conclusion

We think this is absolutely worth a warning in source code, and if in metadata then we should just provide the
default value of the parameter.

#### Span of the expression

Consider this example:

```cs
M(arg1: /* Before */ "A" + /* Mid */ "B"
 /* After */); // What is passed for arg2?
```

There are 3 possible answers for this:

1. The argument expression should refer to the start `arg1:` to the end of the position, either `)` or `,`,
depending on whether the argument is followed by another or not.
2. The argument expression should refer from just after `arg1:` to the end of the position, not including the
argument specifier.
3. We should ignore any trivia, and just have the expression span from the start of the real C# executable code
(the string `"A"`) to the end of the real executable C# code (the end of `"B"`).

While there are legitimate argument for 1 or 2, we don't think they provide enough benefit to make up for the fact
that they will be including leading and trailing whitespace that we don't believe is useful for the users of this
attribute. Given this, we think option 3 is the correct way forward.

##### Conclusion

Option 3: we go from the start of real C# executable code to the end of the expression, not including any leading
or trailing trivia.

### List pattern syntax

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

#### Revisiting syntax

We've heard a lot of community feedback around our existing proposal for length patterns, which looks like this:

```cs
_ = list is [0]; // List has length 0;
```

Top among user feedback is that this syntax is:

1. Confusing. Even among users who tend to give the LDM the benefit of the doubt with syntax choices, we've heard
vociferous feedback that this is not clear and that there is not a clear enough parallel to array creation length
specifiers to make this obvious.
2. Unnatural for the base case. The traditional recursive pattern that languages with strong pattern matching
constructs use is some number of cases that pull out interesting bits, and then a base case to handle the empty list.
Unfortunately, `{ }` is _not_ the empty list case, despite being what otherwise appears to be an empty list pattern.
While in some cases this happens to work because all that's left to handle is when the input is non-null, we don't
think it will lead to clear code.

A smaller group met to try and brainstorm some approaches to solving the issue. These are:

1. Return to the original proposal syntax, using square brackets (`[]`) to denote a list pattern. This breaks with
the correspondence principle, but it does have stronger parallel with other languages, has a natural base case, and
we could potentially add a new creation form that achieves correspondence (and take the time to address things like
`ImmutableArray<T>`, which cannot be initialized by collection initializers today).
2. Use a separator at the end of a list pattern, such as `;`: `{ 1, 2, 3; } or { ; }`. This separator would be
required, giving a few advantages:
    1. Because the separator is always required, the base case looks like the shortest version of the pattern.
    2. Allows list and property patterns to be combined into a single block.
    3. Gives us an avenue to allow collection and property initializers in the same block, by reusing the same syntax
    later.
3. Keep the status quo. Users will get used to the syntax.

These suggestions led to spirited debate. An unfortunate truth here is that, no matter what approach we take, we have
discrepency with some aspect of the language. The semicolon separator approach allows us to mostly keep in line with
collection initializers, but the trailing `;` being required is very different and a wart. Square brackets, on the other
hand, are _very_ different from the rest of C#. Today, square brackets are used for indexing operations and for
specifying the length of an array. Nothing in C# uses them to denote a group of things that is a collection. There are
proposals to use these brackets for an improved version of collection initializers though, giving us an opportunity for
future fulfillment of the correspondence principle, even if it won't be fulfilled on initial release. Patterns also
already have some discrepency with the rest of C#, particularly around `and/or/not` patterns, which aren't words used
in the rest of the language.

##### Conclusion

We will go with option 1: using square brackets for the list pattern. We still need to decide if and how these can be
combined with recursive patterns, but it gives us the most flexibility with regard to future regularity in the language.

#### Length patterns

Orthogonally, we have also come up with a few suggestions for the length pattern:

1. Recognize a special `length` keyword as a property pattern: `{ length: 10 }`. When a type is Countable, this
property is available, and it will bind to `Length` or `Count` as appropriate.
2. Recognize the `Length` and `Count` properties on:
    1. Types that are countable
    1. Types that are both countable and indexable
3. Keep the status quo.

Given that we've chosen square brackets for our new list pattern syntax, option 3 is out. This leaves us with option
1 or 2. We originally wanted special length patterns in the language because we wanted list patterns to work on a type
that didn't have a `Length` or `Count` property: `IEnumerable`. While we still want to do this, the implementation work
is quite complex and we think that it might not get into the initial version. So, while we're not ruling out 1, we don't
think it's necessary quite yet.

Option 2 is nice, but it has a couple of wrinkles. First, it's a breaking change, because we specially recognize that
the property in question cannot be negative. This can affect flow analysis and introduce warnings or errors about
unreachable patterns, and remove warnings about non-exhaustive switch expressions. It's not pretty, but we think we can
tie this recognition to a warning wave. It will be the first time a warning wave _removes_ warnings, instead of adding
them, but we think it's the right move. Second, what types should we specially recognize here. Countable is a very broad
definition in C#: it pretty much just means has an accessible property named either `Count` or `Length`. We think that's
too broad for general recognition; while collections should never have negative lengths, the word `Count` or `Length` on
its own is not strong enough evidence that the type is a collection. Instead, we think we should require both countable
and indexable, the same requirements for using a list pattern in the first place. This will ensure that the type at least
behaves like a collection, and while there still might be such types that return negative `Count`s or `Length`s, patterns
are only one place where such types will confuse their users and we don't think it's an edge case that should derail the
whole feature.

##### Conclusion

We will specially recognize the `Count` and `Length` properties on types that are both countable and indexable, assuming
that it can never be negative.

#### Timing

Given that the changes we've made today are specifically driven by community feedback, we feel that this feature needs
more bake time than is left in the C# 10 cycle. The feature will ship in preview, either with C# 10 (like static abstracts in
interfaces) or shortly after 10 is released. We want to make sure that the course-corrections we're making here help
community understanding of the feature, and we don't have enough time before C# 10 is released to implement the changes
and get them in customer hands before 10 is declared final.
