meetings/2025/LDM-2025-01-22.md
Champion issue: https://github.com/dotnet/csharplang/issues/9058
Specification: https://github.com/dotnet/csharplang/blob/305c5195ce36c9fe66e83235a15c5b9b885e77ab/proposals/partial-events-and-constructors.md
First up today, we did a very quick check in on partial events and constructors. When we looked at partial properties in C# 13, we gave a brief consideration of the remaining member types, but decided to wait until we had use cases. We now have those use cases, and we agree that they're sufficient motivation and validation to move forward with. We'll review the complete specification at a later point, but this proposal is approved and moved into Working Set.
Approved, moved into Working Set
Champion issue: https://github.com/dotnet/csharplang/issues/8887
Specification: https://github.com/dotnet/csharplang/blob/305c5195ce36c9fe66e83235a15c5b9b885e77ab/proposals/collection-expression-arguments.md#construction
Next, we took a look at some of the semantics around collection expression arguments. We did not go over syntax today; that will
be saved for a future LDM. Instead, we wanted to unblock the implementation's thornier semantic questions around overload
resolution. We had no comments on the standard constructor scenario; it seemed fine. What was more controversial were the second
and third scenarios. For the Create method scenario, there's a concern that some of the existing methods that the BCL would
want to use are not in the form that the proposal requires: some of them use params, so their collections are the last argument
to the Create method, not the first. While the BCL could add another method that just reorders the parameters, we're not a
fan of the idea; we may want to relax the requirements here to allow those methods. We then looked at interface implementations,
and whether we want to expose "constructors" for collection expressions target-typed to these interface types. Unfortunately, one
thing that came during discussion here is a discrepancy in the checked-in specification and the notes; the notes from
August 9th indicate that we don't guarantee
what the underlying type of these interfaces is, while the checked-in specification appears to reflect an older initial approach
from the working group that guaranteed the concrete type to be List<T>. We didn't fully debate the implications of this
discrepancy on the current proposal, but the LDM did tend to lean either towards exposing the underlying constructors of the used
type, or to simply stating that a few constructors were presumed to exist, regardless of the underlying type that ends up being
used. For now, the group will proceed in that direction, and we'll need to bring this back again for further discussion to confirm.
Finally, we thought briefly about dynamic arguments; we don't see any current use cases, and think that there are going to be
some particularly gnarly challenges (for example, if we allow Create methods to have the collection either before or after the
other arguments, how does that impact dynamic binding?). Given this, we will block it for now.
Constructor rules are approved. Create rules are approved, with some caveats that we may need to bring back a looser version
after we do more investigation in the BCL for Create methods we want to approve. The implementation will proceed with interface
constructors for now, but will need to come back to ratify the decision and determine exactly how this works in the future.
dynamic arguments are disallowed.
Champion issue: https://github.com/dotnet/csharplang/issues/8697
Specification: https://github.com/dotnet/csharplang/blob/e145230405eabef04a460003a20825fecce7f4d5/proposals/extensions.md
Syntax examples: https://github.com/dotnet/csharplang/blob/8e5e055737bf8f278adf79176fdd370708e23d23/meetings/working-groups/extensions/disambiguation-syntax-examples.md
In our last topic for the day, we dived right into syntax options for disambiguating extension types. Last time we talked about syntax, we decided we wanted to explore member-focused approaches, and see what that ended up looking like. Now we're back with the results of those explorations, and it runs into a few problems. Specifically, there are some types of members that just don't have existing ways of being referred to in C#. For example, indexers, constructors, operators, and casts cannot be captured into method references today, and we'd have to invent a new syntax to even permit them to be referenced. This causes the member-focused approach to be fairly heavy-weight; it's a big piece of new syntax for a scenario we don't expect will be used much. One possible view is that this syntax could be viewed more broadly; what if we were to use this syntax to allow taking references to members that cannot be referenced today? That would bring unification to the feature and help justify the weight of the new syntax forms. However, we're not certain that this is a road we want to go down, and are wary of even more scope creep on the extensions feature. We're pretty split on this approach.
We then looked at the other example forms that were brought. The LDM is fairly split on the casting form; some think it's a natural
extension, others think that it's too far removed from the existing meaning of casting, as it does not modify the receiver.
Additionally, there is no real opportunity for a cast-like syntax to be useful to other disambiguation scenarios, such as calling
a DIM or an explicit implementation on a struct without boxing. Invocation-like syntax was more positively received, as was
in and at. using was pretty strongly disliked for introducing yet another new meaning for using, which is already
heavily overloaded. @ was also concerning for what impacts it could have on other tooling ecosystems such as Razor.
We did consider a couple more approaches that were not in the document as well:
using MyExtension { range.Where (i => i < 10); }? This is not a new meaning for using, it's just a new
location that an existing import syntax can be used. This is still potentially quite confusing, however, so we're not
enamored with it.range = MyExtension.op_Plus(range, 5);. This has the advantage of requiring absolutely no new syntax, but does mean
that we need to lean further into the "this is just sugar over static methods" approach.Ultimately, we did not make a final decision today. We've expressed some initial opinions, and will come back later with refinements. Ultimately, we do not think an initial preview of extensions is gated on this decision, so we are fine with not having a concrete syntax today.