# C# Language Design Meeting for February 19th, 2025

## Agenda

- [Extensions](#extensions)

## Quote of the Day

- "But, SHOULD we throw the baby out with the bath water?"

## Discussion

### Extensions

Champion issue: https://github.com/dotnet/csharplang/issues/8697  
Specification: https://github.com/dotnet/csharplang/blob/e25de8eaaa30470ecd1202c1b1ce840512b0ff0c/proposals/extensions.md  
Reviewed document: https://github.com/dotnet/csharplang/blob/e25de8eaaa30470ecd1202c1b1ce840512b0ff0c/meetings/working-groups/extensions/extensions-lookup.md

Today, we went over continuing proposals for how to do extension method lookup. This topic is both persistent and difficult, particularly since how extension method
lookup was originally specified is not how it was actually implemented. As originally specified, extension methods invoked in extension form would have been subject
to a 2-phase lookup approach, where first the receiver is used to find compatible extension methods, and then overload resolution would be performed among those
methods. As the LDM, we generally prefer this approach; it is the most consistent with how lookup works for actual instance members. It would also ensure that
"weird" results, like the `out` examples shown in [extensions-lookup], behave as a user might naively expect, failing to unify because the extension is being
called on `I<string>`, not `I<object>`. This potentially gets even more odd for static scenarios:

```cs
var e = IEnumerable<string>.M(out object o); // What type is e? Does this line compile?

public static class Ext
{
    extension<T>(IEnumerable<T>)
    {
        public static IEnumerable<T> M(out T t) => ...;
    }
}
```

However, in C# 3.0, this is _not_ what was implemented. The examples in [extensions-lookup] do not error, they compile, giving `IEnumerable<object>` as the
receiver type. Given the 20 years of history here, and how any change to lookup is nearly certain to break _someone_, we don't feel comfortable breaking existing
extension methods, and may even want to simply update the specification to cover what was really implemented in C# 3.0, rather than what was originally intended.
Further, every attempt we've made during the design process of new extensions at breaking compatibility has led to us scaling back the breaks. At this point,
we're aware of 3 major categories of extension methods that cannot be ported to the new syntax form 1-1 given the current design:

1. Generic extensions that have a receiver that uses one or more of the type parameters and, at the call site, specify the type arguments. This is because they must
   specify _all_ the type arguments, including any for the receiver, and that's not possible with the current design, as the receiver type parameters cannot be
   specified.
2. Generic extensions that have a receiver that uses one or more of the type parameters and specify their type parameters in a non-idiomatic order. For example, a
   hypothetical `TReturn Select<TReturn, TThis>(this TThis t, Func<TThis, TReturn> selector)`. We are not aware of real-world examples of this pattern, and are more
   comfortable ignoring it.
3. The `out` inference issue that was brought up today.

We are, at the very least, concerned about categories 1 and 3. Given this, we want the working group to investigate what a compatibility mechanism would look like
that handles these cases, potentially by changing how lookup works for such members, and whether we can get to a place that needs no explicit compatibility switch,
but instead bakes the compatibility into the language.

[extensions-lookup]: https://github.com/dotnet/csharplang/blob/e25de8eaaa30470ecd1202c1b1ce840512b0ff0c/meetings/working-groups/extensions/extensions-lookup.md#aligning-with-implementation-of-classic-extension-methods
