csharplang/meetings/2021/LDM-2021-10-20.md
2021-10-20 16:27:56 -07:00

5.9 KiB

C# Language Design Meeting for October 20th, 2021

Agenda

  1. Open questions in list patterns
    1. Types that define both Length and Count
    2. Slices that return null
  2. Primary constructors

Quote of the Day

  • "No fingerprints today, I always struggle with it after a bank robbery, when I file them down"

Discussion

Open questions in list patterns

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

We had 3 open questions from tests on list patterns. Questions 1 and 2 both relate to types with both Length and Count properties, so our decision applies to both.

Types that define both Length and Count

Our previous definition of a type that can be matched by a list pattern is that the type must be both indexable and countable. Because countable could be one of two properties, Length or Count, we need to decide what do when a type has both. Our options are:

  1. Treat them as equivalent and assume they return the same value. This will affect subsumption.
  2. Treat the non-recognized one (Count in the case where both Length and Count are defined) as not being special at all.

Conceptually, we think it would be odd to assume that Count is equal to Length, even though we'll never use it for any form of slicing or length checking. While it might be reasonable to assume they return the same value, we don't think it's an important scenario. We'll have nearly a year of preview on this feature, so we have plenty of time to react to feedback if dogfooding shows that it's an important scenario.

Conclusion

We will not treat Length and Count as being connected when both are defined.

Slices that return null

We believe that well-defined slice methods should never return null for an in-bounds range, and we'll never generate a call to such a slice method with an out-of-bounds range. However, we also don't think there's a customer for this scenario: a search of GitHub showed no Slice methods that return an annotated value, and treating a slice method differently than its annotation would require both implementation effort and customer education. Given that there are no known cases this affects, we don't think it's worth it. Similarly to the first question, if such scenarios are identified during preview, we can correct appropriately.

Conclusion

If a Slice method is annotated, we will treat it as potentially returning null for the purposes of subsumption and exhaustiveness.

Primary constructors

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

Now that records have been out for a year and we've expanded them to struct and reference types, we think it's time to start looking at primary constructors again. We updated the proposal with the new syntax forms from our records work, removing or modifying components where they make sense. Unlike for records, primary constructors aren't about defining the members that make up a grouping of data: instead, they're purely for defining the inputs to a class. So things like deconstruction, equality, and public properties aren't automatically generated from the parameters. Instead, they become fields, that are then eliminted if the field is never used outside of a field initializer.

We think there are a couple of open discussion topics:

  1. Should the fields be readonly or not?
  2. How important are primary constructor bodies for a generalized feature?

For question 1, we think that, for better or for worse, C# is a mutable-by-default language. We were able to change the defaults for record types because mutability in a value-based reference type is actively harmful, but we have to consider a number of naming and mutability issues we were able to skirt around in record types. Field naming conventions, for example, are heavily split in C#, with some users using a leading _, and some preferring to just use pascalCase with no leading modifiers. We think readonly is similar: if users would like to modify the defaults of C#, they can define their own field and do so. If this proves to be the wrong default we can make a change before it ships for real, or potentially invest in https://github.com/dotnet/csharplang/issues/188 to allow putting modifiers on parameters.

For question 2, we think that it will be important, but that once https://github.com/dotnet/csharplang/issues/2145 is in the language, a good portion of initialization code will be expressable in just a single initialization expression. It will miss some more complex scenarios, but we think just primary constructors are a good first step.

In general, we also want to make sure we're defining the difference between primary constructors and required properties well. With the potential for multiple new initialization features to ship in a single language version, we want to make sure they complement each other well. We think required properties work well for public contracts: DTOs, POCOs, and other similar, nominal data structures that will expose these properties externally. Primary constructors, on the other hand, are for other types, that do not define public surface area based on their parameters. Instead, they simply define input parameters, and can assign them to private fields or otherwise manipulate them as they choose.

Some concrete feedback on the proposal:

  • The exception for calling this for copy constructors feels premature, as they don't mean anything to any type except record types. If we generalize with in the future, then the exception will make sense, and we can add it then.
  • We should disallow methods with the same name as primary constructor parameters. This is confusing and while it could potentially be understandable code if the parameter was never captured, could then have spooky action at a distance if the parameter was accidentally captured when a user forgot to write the () of the method name.

Conclusion

These words are accepted.