72 lines
4.9 KiB
Markdown
72 lines
4.9 KiB
Markdown
# C# Language Design Meeting for October 5th, 2020
|
|
|
|
## Agenda
|
|
|
|
1. [`record struct` primary constructor defaults](#record-struct-primary-constructor-defaults)
|
|
2. [Changing the member type of a primary constructor parameter](#changing-the-member-type-of-a-primary-constructor-parameter)
|
|
3. [`data` members](#data-members)
|
|
|
|
## Quote of the Day
|
|
|
|
- "The problem with people who voted for immutable by default is that they can't change their opinion. #bad_dad_jokes"
|
|
|
|
## Discussion
|
|
|
|
### `record struct` primary constructor defaults
|
|
|
|
We picked up today where we left off [last time](LDM-2020-09-30.md#primary-constructors-and-data-properties), looking at what
|
|
primary constructors should generate in `record struct`s. We have 2 general axes to debate: whether we should generate mutable
|
|
or immutable members, and whether those members should be properties or fields. All 4 combinations of these options are valid
|
|
places that we could land, with various pros and cons, so we started by examing the mutable vs immutable axis. In C# 9, `record`
|
|
primary constructors mean that the properties are generated as immutable, and consistency is a strong argument for preferring
|
|
immutable in structs. However, we also have another analogous feature in C#: tuples. We decided on mutability there because it's
|
|
more convenient, and struct mutability does not carry the same level of concern as class mutability does. A struct as a
|
|
dictionary key does not risk getting lost in the dictionary unless it itself references mutable class state, which is just as
|
|
much of a concern for class types as it is for struct types. Even if we had `with` expressions at the time of tuples, it's
|
|
likely that we still would have had the fields be mutable. A number of C# 7 features centered around reducing unnecessary struct
|
|
copying, such as `readonly` members and ref struct improvements, and reducing copies in large structs by `with` is still a
|
|
useful goal. Finally, we have a better story for making a `struct` fully-`readonly` with 1 keyword, while we don't have a similar
|
|
story for making a `struct` fully-mutable with a similar gesture.
|
|
|
|
Next, we examined the question of properties vs fields. We again looked to our previous art in tuples. `ValueTuple` can be viewed
|
|
as an anonymous struct record type: it has value equality and is used as a pure data holder. However, `ValueTuple` is a type
|
|
defined in the framework, and its implementation details are public concern. As a framework-defined pure data holder, it has no
|
|
extra behavior to encapsulate. A `record struct`, on the other hand, is not a public framework type. Much like any other user-
|
|
defined `class` or `struct`, the implementation details are not public concern, but the concern of the creator. We have real
|
|
examples in the framework (such as around the mathematics types) where exposing fields instead of properties was later regretted
|
|
because it limits the future flexibility of the type, and we feel the same level of concern applies here.
|
|
|
|
#### Conclusion
|
|
|
|
Primary constructors in `record struct`s will generate mutable properties by default. Like with `record` `class`es, users will
|
|
be able to provide a definition for the property if they do not like the defaults.
|
|
|
|
### Changing the member type of a primary constructor parameter
|
|
|
|
In C# 9, we allow `record` types to redefine the property generated by a primary constructor parameter, changing the accessibility
|
|
or the accessors. However, we did not allow them to change whether the member is a field or property. This is an oversight, and
|
|
we should allow changing whether the member is a field or property in C# 10. This will allow overriding of the default decision
|
|
in the first section, giving an ability for a "grow-up" story for tuples into named `record struct`s with mutable fields if the
|
|
user wishes.
|
|
|
|
### `data` members
|
|
|
|
Finally today, we took another look at `data` members, and what behavior they should have in `record struct`s as opposed to
|
|
`record` `classes`. We had previously decided that `data` members should generate `public` `init` properties in `record` types;
|
|
therefore, the crucial thing to decide is if `data` should mean the same thing as `record` would in that type, or if the `data`
|
|
keyword should be separated from `record` entirely. In C# today, we have very few keywords that change the code they generate
|
|
based on containing type context, and making `data` be dependent on whether the member is in a `struct` or `class` could end up
|
|
being quite confusing. On the other hand, if `data` is "the short way to create a nominal record type", then having different
|
|
behavior between positional parameters and `data` members in a `struct` could also be confusing.
|
|
|
|
#### Conclusion
|
|
|
|
We did not reach a decision on this today. There are 3 proposals on the table:
|
|
|
|
1. A `data` member is `public string FirstName { get; set; }` in `struct` types, and `public string FirstName { get; init; }` in
|
|
`class` types.
|
|
2. A `data` member is `public string FirstName { get; init; }` in all types.
|
|
3. We cut `data` entirely.
|
|
|
|
We'll come back to this in a future LDM.
|