Back to Csharplang

C# Language Design Meeting for October 2nd, 2024

meetings/2024/LDM-2024-10-02.md

latest4.9 KB
Original Source

C# Language Design Meeting for October 2nd, 2024

Agenda

Quote of the Day

  • "Attempting to find my way to this room was like navigating the Severance basement"

Discussion

Open questions in field

Champion issue: https://github.com/dotnet/csharplang/issues/140
Questions: https://github.com/dotnet/csharplang/blob/3cca17a650e40d787629e52a8d46e59459cb2b74/proposals/field-keyword.md#open-ldm-questions

readonly contexts and set

There could be scenarios for this where you're implementing a set accessor on a readonly struct and either passing it through, or throwing. Recommendation is accepted, we will allow this.

Conditional code

Conditional code can have effects on non-conditional code, such as Debug.Assert changing nullability. It would be strange if field didn't have similar impacts. It is also unlikely to come up in most code, so we'll do the simple thing and accept the recommendation.

Interface properties and auto-accessors

Standardizing around the instance field itself being the cause of the error is consistent with partial properties in classes, and we like that outcome. The recommendation is accepted.

Extensions

Champion issue: https://github.com/dotnet/csharplang/issues/5497
Related: https://github.com/dotnet/csharplang/blob/1e8255a438517bc3ad067c726c28cfa20cb60f1e/meetings/working-groups/extensions/extensions-as-static-types.md, https://github.com/dotnet/csharplang/pull/8472

Today, we continued our thinking around extensions from last time, and around how we want to approach the complex issues standing in the way of us getting previews out. Unfortunately for us, this ended up raising far more questions than answers. The focus today was around aligning with method type inference for existing extension methods. As an example:

cs
public class C
{
    public void M(I<string> i, out object o)
    {
        i.M(out o); // infers E.M<object>
        i.M2(out o); // error CS1503: Argument 1: cannot convert from 'out object' to 'out string'
    }
}
public static class E
{
   public static void M<T>(this I<T> i, out T t) { t = default; }
}
public static extension E2<T> for I<T>
{
   public static void M2(out T t) { t = default; }
}
public interface I<out T> { }

As currently envisioned, extension types have a 2-stage type inference approach. The first stage deduces the type parameter for the extension type, based on the receiver, and the second deduces any type parameters on the method (if there are any). This is very different to how today's extension methods work; since all type parameters are on the method, they're all inferred at the same time, and the receiver is just one of the parameters. This is an example of a behavior difference that we'd incur between old and new extension methods, and we want to see if we can solve it. One possibility is a single pass, that pretends the type parameters from the extension type are part of the method signature. Then we do the same single pass as in extension methods today, and it all behaves the same.

This comes with its own downsides, however, which is breaking a different expectation from C# users around how type parameters work. There is no place in C# today where type parameters from different definitions are smashed together and inferred at the same time. A single-stage inference pass will look and feel different to how the syntax implies that it would work. Some members feel that the two-stage approach is the more logical approach in general, and actually appreciate that VB did this with classic extension methods.

We then further got into discussions around compat, and how/if existing extensions methods could move to the new form. As an example, could LINQ be written in the new form? We're somewhat concerned that it couldn't be, at least not as designed today; there's an Enumerable class with a large number of extensions in it, many on differing types. Adopting the new form would of course at least break binary compat, since we can't define all those methods in a single extension type. We'd then further be concerned that this would block the BCL from using the new form in conjunction with the old form, due to concerns about the differing inference methodology, as well as other behavioral differences between the forms.

This has finally brought us squarely back to the questions on syntax. If the current syntax implies a particular behavior, and that behavior would block adoption, perhaps a different form is needed. We could consider a more member-centric approach, rather than a type-centric approach. We're definitely not ready to make any calls on this today, but we shall examine these ideas further in the next LDM.