# C# Language Design Meeting for July 31st, 2023

## Agenda

- [Primary constructor parameters and `readonly`](#primary-constructor-parameters-and-readonly)

## Quote of the Day

- "Maybe I damaged my brain in the sun"

## Discussion

### Primary constructor parameters and `readonly`

https://github.com/dotnet/csharplang/issues/2691  
https://github.com/dotnet/csharplang/issues/188  
https://github.com/dotnet/csharplang/discussions/7377  
https://github.com/dotnet/csharplang/blob/bc2c69e6f2bd28373e341effaa1182ebcb7ea72c/proposals/readonly-parameters.md

We picked up again from [last Wednesday](LDM-2023-07-26.md#primary-constructor-parameters-and-readonly). To restate the options we'd started with in that meeting:

1. Do nothing. Leave primary constructors as mutable, and do not plan on addressing this. If a user wants to enforce readonly class state, they should
   declare a class member (field or property), explicitly assign into that member, and use that member, not the parameter.
2. Plan on adding a `readonly` keyword. This keyword may come with C# 12, but might also be delayed until C# 13. There are open questions as to
   what this keyword would mean: would it follow field `readonly`, where the parameter capture would actually be mutable during initialization, or not?
   But by default, primary constructor parameters would always be mutable unless otherwise specified.
3. Follow the default established by record primary constructors. This would mean that all primary constructor parameters would be mutable during
   initialization (as in all `record` types today), but for `class`es and `readonly struct`s, they would be readonly after construction.
4. Have all primary constructor parameters on non-record types be always `readonly`.

We'd eliminated option 1 and 4 during that meeting (4 very decisively, 1 not very decisively), leaving options 2 and 3. One important clarification we made in this
meeting, though, was that 1 and 2 are not actually mutually exclusive. Many members of the LDM would be fine shipping C# 12 with 1, and then doing a `readonly` keyword
in C# 13. So we're really debating between 2 and 3, but not necessarily committing to doing 2 before C# 12 ships.

The main thrust of the debate between 2 and 3 is the presence of opinion in mainline C# code that is different than C# opinion elsewhere in mainline code. Parameters
are always mutable by default in C#, as are fields; it's only in automatically generated `record class` that we default to `readonly` (or more specifically, `init`).
So no matter where we end up on this issue, we have a decoder ring for users: either we need to explain why we chose to make primary constructor parameters `readonly`,
or why we chose to make `record`s different than the rest of the language. This distinction revealed an important point in the debate, however: are we bringing primary
constructors, as created by `record`s, back to the rest of the language, or are we bringing existing creation patterns forward with primary constructors, and viewing
`record`s as a step further?

One thing LDM was immediately unanimous on was not wanting a `mut` or `mutable` keyword, even if we were to go with option 3. We don't have such a concept elsewhere
in the language, and needing to add it here is immediately concerning. We also investigated whether we can confidently promote primary constructor parameters to full
type members in a simple fashion: could we, for example, have `private` automatically turn into a `private readonly` field, and `public` automatically turn into a
`public { get; init; }` property? We're not sure about this either: `private` seems fine, but the `public` version feels iffy. Is `init`, as in `record`s, the right
default? And if it's not, is that yet another area where we'll need to explain the differences between `record`s and non-`record`s?

In terms of evolution, there also a number of LDM members who are concerned about the slippery slope that is evolution in the first place. How far should we plan on
taking this? We start with simple member declarations, but then those declarations might need validation, default values, differing public and private names, the
ability to specify partial when we eventually allow `partial` properties, etc. At what point do we say that we've gone far enough and that any further member-like
things need to be actual type members? It's clear that will be a question for our future selves, and that it's unclear where we'll land when we get there.

We finally turned back to the question of what direction of evolution we are intending for primary constructor parameters. Another way of looking at this question is:
if we had flipped the design order, delivering primary constructor parameters before `record`s, instead of the other way around, would we be doing anything differently?
We don't think so: when we look at how we want to explain the language, we think that there's a core set of rules around mutation that apply everywhere in the language,
_except_ for `record`s. `record`s exist to allow C# users to work with data more easily, and they contain a number of opinions that make this easier, from value-based
equality to `readonly`ness for reference types. We think that this is the C# we want to explain; the alternative would be explaining that primary constructors differ
from the rest of C# by bringing in opinions from `record` types. It would potentially be easier to explain to existing users, but harder to explain the language
philosophy as a whole. We therefore conclude that primary constructor parameters will ship in C# 12 as is. We will prioritize work on a `readonly` modifier for primary
constructor parameters, including figuring out whether this modifier will mean `readonly` like it does for fields, where it is mutable during initialization, or if
will mean fully `readonly`, with no ability to mutate at all. This may not make it into C# 12, but we will target early previews of C# 13.

#### Conclusion

Primary constructor parameters will ship as mutable-by-default, and we will investigate the `readonly` parameter modifier shortly.
