csharplang/meetings/2021/LDM-2021-04-21.md
2021-07-27 12:34:38 -07:00

6.4 KiB

C# Language Design Meeting for April 21st, 2021

Agenda

  1. Inferred types for lambdas and method groups
  2. Improved interpolated strings

Quote of the Day

  • "And then we will open Pandora's box"

Discussion

Inferred types for lambdas and method groups

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md
https://github.com/dotnet/csharplang/issues/4674

Further implementation work has revealed a few potential breaking changes in giving lambdas and method groups a natural type (linked in the issue above). It is possible to run into these breaks with or without extension methods involved, and while these are the same types of breaks that can be observed in extension method resolution (adding new ones in a closer scope or adding an instance method that is preferred), these would be a case of the language actually changing the results of overload resolution for the same inputs. We have a few ideas for how to approach this:

  1. Accept the break. This would be one of the bigger breaks that C# has ever taken, on par with the foreach-scoping changes in C# 5.
  2. Always prefer a target-type for method groups and lambda expressions. This idea, while sounding simple, is quite complex implementation-wise. The C# type inference and overload resolution algorithms are very much tied to having a natural type for an expression, and flowing that information through the process. To prefer target-typing here, we would have to first try to target-type, then fall back to a natural type and try the whole process again. This would add a significant cost complexity to an already-expensive part of the compilation process, and while it might work it will be an exponentially harder problem every time we want to do something else like this.
  3. Make natural types for lambdas and method groups opt-in. This would be through some sort of syntax gesture, such as:
    1. A cast like (_) to indicate "I would like the natural type for this but I don't want to state it". This would lock in a specific syntax that would forever need to be explained to users to "use C# 10 please".
    2. Only lambdas with return types (indicating new feature uptake) would have a natural type. This would severely hamper the scenarios our partner teams are interested in: most of the time, their return types should be inferred, and it will not support method groups.
  4. Only infer a natural type when converting to System.Delegate or System.MulticastDelegate. This technically still does have the chance of breaking scenarios, but the breaks are significantly smaller. However, this would result in the unfortunate consequence that only non-generic delegate types can cause natural types to be inferred, which feels like a pre-generics solution to the problem.

While accepting a break here could potentially be big, it's important to note that this particular scenario is actually quite fragile today. In either of the code samples, simply extracting the lambda or method group to a local variable will cause overload resolution to pick the same overload that giving the lambda a natural type would. This is because the local variable will have a natural type itself. It seems relatively likely that the only users intentionally taking advantage of this "feature" are creating extension methods that just call the original method with the delegate, using the extension method as a source of a target-type for the lambda expression.

We also have a decent amount of runway for previewing changes here. We're not planning on shipping this feature in a non-preview version of either VS or the runtime until it's time to actually release C# 10, so we can make an aggressive version of this change now and see if we need to dial it back. The preview will have a warning reported whenever the natural type of a lambda or method group is used, which will hopefully give us enough of a signal during this preview to know just how aggressive we can be here.

Conclusion

We will tentatively accept the breaking change to overload resolution. Previews will be used to determine if we need to revisit this decision.

Improved interpolated strings

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

We took a look at a couple of outstanding issues in interpolated strings today. First, we looked at the order of arguments to the builder from the current context. There is potential here to break lexical ordering of expressions if we allow arguments to the builder's Create method to come after the builder itself in the argument list. While a possible model for thinking of interpolation holes is lambda expressions, where the holes are either not evaluated or evaluated at a later point from where they were written, these holes can potentially have definite assignment impacts on the current method body. This is entirely different from other forms of deferred execution in C#, and we're concerned with the implication. After discussion, we agreed on requiring lexical ordering to be respected: if a parameter is an argument to the Create method of the builder type, it must come before the interpolated string. That error will be reported at the invocation site, and it should be affected by the true lexical order of the invocation. Named parameters can be used to reorder evaluation, so we should error if named parameter use results in an argument being evaluated after the interpolated string that needs it. We will implement a warning at the method declaration if the signature of the method requires named parameters to need to be used at the call site.

We also looked at the proposal around allowing interpolated strings to be "seen" through conversion expressions and through binary addition expressions. Binary addition being proposed resulted from prototyping work in the runtime to use the builder, while the conversion proposal was a logical next step. After discussion, we don't think that the use case of needing to disambiguate between two overloads with different builder types and otherwise identical signatures is realistic. We accept the use case for binary addition, but we will only do it for conversions to interpolated builder types as we don't have an ask for IFormattable or FormattableString here.

Conclusions

Lexical ordering should be respected, and seeing interpolated strings through binary addition expressions will be supported.