csharplang/meetings/2021/LDM-2021-05-03.md
2021-05-04 11:45:34 -07:00

5.4 KiB

C# Language Design Meeting for May 3rd, 2021

Agenda

  1. Improved interpolated strings
  2. Open questions in record structs

Quote of the Day

  • "You all have a special way of working"

Discussion

Improved interpolated strings

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

Today, we got through the last of the open questions around the improved interpolated strings feature proposal.

Create vs .ctor

The first open question is around using the Create method instead of a constructor call. The spec today uses a static Create method as an abstraction, to allow a hypothetical builder type to pool reference types in a static context, rather than being forced to return a new instance on every builder creation. However, while this is a nice abstraction, we don't know of any proposed builder types that would take advantage of this feature, and there are real codegen benefits to using a constructor instead. If we later come across a use case that would like a static Create method instead, we can also add it later by using a switch on the attribute.

Conclusion

For better codegen, we will go with a single method of creation, a constructor, not a Create method.

Converting regular strings to builder types

We could consider any string value as being convertible to a builder type automatically via the builder pattern, which is the trivial case of value.Length number of characters with zero interpolation holes. If we were to do this, it would be in the interest of consistency: a user could more readily reason about how all string types will interact with an API. However, we do already have a mechanism in the language to enable this: implicit user-defined conversions. Pushing a string through the standard builder pattern will likely be worse for performance than what an API-author would write in an implicit conversion, and user-defined conversions already have well-defined semantics and impacts on overload resolution.

Conclusion

Rejected. If an API author wants string to be convertible to their builders, they can use a user-defined conversion.

ref struct builder types in async contexts

Today, ref struct types are generally forbbiden in async contexts. This is because await expressions can occur in any subexpression, and we have not formalized rules around ensuring that a ref struct variable does not cross an await boundary. In order to enable ref struct builder types in async methods, we'd need to do essentially that same work in order to make usage safe. We're not introducing any new tradeoffs by avoiding this: already today, developers need to choose between using a ref struct, and allowing usage in async contexts. While we would be interested in general rules here, we don't think we should do anything special with regard to interpolated strings builder types. If we enable it in general, interpolated string builders will naturally come along for the ride.

Conclusion

We will treat interpolated string builder usage as any other usage of a ref struct in async methods (today, that means disallowed).

Open questions in record structs

Implementing ISpanFormattable

The BCL is adding a new interface for .NET 6, ISpanFormattable, which allows types to be formatted directly into a span, rather than having to create a separate string instance. One immediate thought we have is why are records (struct or class) special here? We often think of records as being named versions of anonymous types/tuples, so if we were to do this for records we would likely want to do this for those types as well. This is important because it does impact public API: new overloads of PrintMembers will need to be implemented, and how this will interact with the existing PrintMembers method that takes a StringBuilder is unclear. We also don't have a clear scenario here: ToString() in records is mainly focused around debugging and logging scenarios. While performance is a nice to have here, it's not an overridding concern, and users who want to actually use ToString() to do more important work can provide their own implementation, and ISpanFormattable with it. They can handle the problems around interop with existing APIs by simply not using those APIs, which will keep the considerations simpler for them.

Conclusion

Rejected.

Using InterpolatedStringBuilder in record formatting

We could potentially optimize the implementation of ToString() by using InterpolatedStringBuilder as the string-building mechanism, rather than StringBuilder. Unfortunately, this again runs into the problem of needing to interop with existing code, which unfortunately limits our options here. Without a clear scenario to try and solve, we don't think this is worth it.

Conclusion

Rejected.

Passing an IFormatProvider

We could potentially generate an overload of ToString that takes an IFormatProvider and passes it to nested contexts. This again runs the question of lacking a clear scenario and interop with existing code. We additionally don't have a clear idea of when we'd pass the format provider to a nested type, as there is no standardized ToString(IFormatProvider) method on all types. If a user wants to have formatted strings in their records, they presumably are providing their own implementation anyway, so we don't feel this is appropriate.

Conclusion

Rejected.