# C# Language Design Meeting for May 7th, 2025

## Agenda

- [Collection Expression Arguments](#collection-expression-arguments)
    - [Interface target type](#interface-target-type)
    - [Constructor binding behavior](#constructor-binding-behavior)
    - [Syntax](#syntax)

## Quote of the Day

- "`with(out var x)` sounds like we're leaving `var x` behind"

## Discussion

### Collection Expression Arguments

Champion issue: https://github.com/dotnet/csharplang/issues/8887  
Spec: https://github.com/dotnet/csharplang/blob/0eddbeca5dc6edfbf91f915ecea3379b8e337cc7/proposals/collection-expression-arguments.md

#### Interface target type

Spec section: https://github.com/dotnet/csharplang/blob/0eddbeca5dc6edfbf91f915ecea3379b8e337cc7/proposals/collection-expression-arguments.md#interface-target-type

We started today by looking at the proposed set of constructors for interface types to ratify the list. The main bit of feedback here was on the readonly types; the proposal
does not accept capacity for any of these types, and we briefly explored whether it might be useful to do so. We don't know of any examples where this would be useful from a
runtime perspective, but we did want to consider whether it would allow lower code churn when switching from a mutable interface to a readonly interface. Ultimately, we don't
think that this is useful, and we approve the constructors as proposed.

##### Conclusion

Interface constructor list approved as proposed.

#### Constructor binding behavior

Spec section: https://github.com/dotnet/csharplang/blob/0eddbeca5dc6edfbf91f915ecea3379b8e337cc7/proposals/collection-expression-arguments.md#constructors

Next, we looked at the rules for binding constructors and create methods in general. A particular wart that has come up as part of this is that we may end up skipping optional
parameters as part of a signature without any user-specificity; further, users may decide that they need to mark parameters as optional even if they normally wouldn't do so, so
that they can mark the collection as optional in their `Create` methods. For example, a user might have a method like this:

```cs
public static MyCollection<T> Create<T>(IComparer<T> comparer = null, ReadOnlySpan<T> elements = default);
```

Here, users are forced to mark `elements` as optional so that they can mark `comparer` as optional, even though they might not want to do so. Namely, this design might force
API designers into non-standard approaches so that their methods compile. There are also questions around `Create` methods that take multiple `ROS` elements: which should be the
one we use for the elements? While there were good reasons to try and prefer the ROS as the last element in the argument list, we do also think that moving it to be the first
element would be beneficial for these questions, so we want the working group to go and take a look at this question again.

We also looked at `out` parameters and `params` parameters. We're ok with the meaning of these being the same in collection expression arguments. as currently defined, `params`
is mainly useful in constructors, not in `Create` methods. We will not allow `params` parameters to be split across collection expression arguments and the collection expression
itself.

##### Conclusion

`out` and `params` are approved. The working group will revisit the rules around collection builder signature matching, and whether we can put the ROS first.

#### Syntax

Finally, we got to a long-simmering question: what is the real syntax we want to use for collection expression arguments. `with` was intended as a way to move forward with the
various semantics, without needing to block implementation. We have always intended to revisit it, and now we're doing so. We threw a lot of syntaxes into a block to give
inspiration:

```cs
// Trailing arguments
[a, b, c] with (comparer)

// Leading arguments
new(comparer, ......) [...]
with(comparer, ......) [...]
init(comparer, ......) [...]
args(comparer, ......) [...]

// Direct parentheses - conflicts with tuples in the wild
[ (a, b, c), 1, 2, 4]

// new as the keyword - conflicts with target-typed new in the wild
[  new(a, c, c), 1, 2, 4]

// Sigils - no conflict, but is it confusing?
[ @(a, b, c), 1, 2, 4]

// Already used for record mutation
[ with(a, b, c), 1, 2, 4]

// Already used for property setters
[ init(a, b, c), 1, 2, 4]

// Already used for main method arguments in top-level statements
[ args(a, b, c), 1, 2, 4]

// Previous examples, using semi-colons instead of commas
[ (a, b, c); 1, 2, 4]
[ with(a, b, c); 1, 2, 4]
[ init(a, b, c); 1, 2, 4]
[ args(a, b, c); 1, 2, 4]
```

To make it easier to start whittling down our options, we created some broad categories. First up, we looked at the location of the arguments; before the expression, inside the
expression, or after the expression? We don't like after, due to order of operations, as it implies that the collection contents have to be evaluated entirely before the
arguments can be evaluated. Before is better, but we don't like the mental stutter that is involved: you start reading an argument list, then see the `[` and realize what you
read previously were the arguments for a collection expression, not a method call. Inside was the clear winner in our eyes.

Next, we looked using a sigil, or a keyword. We very unanimously felt that a sigil was not C#-y, and too cryptic. Therefore, we will use a keyword.

Next, we looked at the separation sigil. Our two leading contenders are `,` and `;`. `:` was eliminated early because it is used as the dictionary expression `key:value`
separator. Between `;` and `,`, we lean slightly towards the `,`. The `;` is a harder separation, but we also don't have any usage of `;` inside an expression today, besides
statements inside block-bodied lambdas, and we're not sure that this is a good location to change that. `,` does come with less visual separation, but we're very used to using
`,`s inside expressions, which is a positive. We're leaning in the `,` direction, and will come back next time to settle for certain.

Finally, in the waning minutes of the meeting, we looked at the various keywords that were proposed. The first choice of most LDT members was either `with` or `args`, and `init`
as a distant 3rd. We'll come back next time as well to settle between `with` and `args`.

##### Conclusion

The syntax will definitely be inside the collection expression at the front, and use a keyword of some kind. We've narrowed separator options to `,` vs `;`, and keyword options
to `with` vs `args`. We will settle those choices next time.
