Add language design notes for Dec. 11, 2019
This commit is contained in:
parent
42ef673ecc
commit
d5a784db27
117
meetings/2019/LDM-2019-12-11.md
Normal file
117
meetings/2019/LDM-2019-12-11.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
|
||||
# C# Language Design Meeting for Dec. 11, 2019
|
||||
|
||||
1. Design review feedback
|
||||
|
||||
## Discussion
|
||||
|
||||
We got feedback from the design review that we shouldn't try to conflate too many problems. If
|
||||
we want to make it easier to support structural equality, we should see if it's possible to
|
||||
address directly, without requiring the other features of records. One suggestion was to take
|
||||
inspiration from `VB`, which allows the `key` modifier to be added to VB anonymous types to
|
||||
indicate structural equality with the members used as the keys.
|
||||
|
||||
We took that advice and looked at a sketch of what that could look like:
|
||||
|
||||
```C#
|
||||
class C
|
||||
{
|
||||
public key string Item1 { get; }
|
||||
public string Item2 { get; }
|
||||
}
|
||||
```
|
||||
|
||||
The `key` modifier would be used to control generated equality, such that all members marked
|
||||
`key` would be compared for equality in an `Equals` override (using the same pattern as in the
|
||||
original records proposal).
|
||||
|
||||
The above code sample certainly looks simple, but unfortunately it's not sufficient for
|
||||
real-world code. Both `Item1` and `Item2` are `get`-only autoproperties, meaning that as-is there
|
||||
is no way to initialize those members. A working example looks more like:
|
||||
|
||||
```C#
|
||||
class C
|
||||
{
|
||||
public key string Item1 { get; }
|
||||
public string Item2 { get; }
|
||||
public C(string item1, string item2)
|
||||
{
|
||||
Item1 = item1;
|
||||
Item2 = item2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is significantly longer than the original sample, grows with the number of properties, and
|
||||
is repetitive. The latter is particularly problematic, as repetitive boilerplate is often a source
|
||||
of hard-to-see bugs or typos.
|
||||
|
||||
Worse, we've seen that when construction becomes laborious, users resort to making their types
|
||||
mutable instead of writing out the constructor, e.g.
|
||||
|
||||
```C#
|
||||
class C
|
||||
{
|
||||
public key string Item1 { get; set; }
|
||||
public string Item2 { get; }
|
||||
}
|
||||
```
|
||||
|
||||
This is unfortunate in most code, but it's worrying when combined with structural equality. When
|
||||
used in Dictionaries, mutable types with structural equality are an anti-pattern because the hash
|
||||
code of the type changes, causing the type to "disappear" from the Dictionary. It's one thing if
|
||||
the user opts-in to this risk, knowing that they need to be careful, and a completely different
|
||||
situation if the language encourages a dangerous pattern.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
This is an interesting design point that we think we'll incorporate. We've also agreed that we
|
||||
have a hard design requirement: if we provide a feature for easy structural equality, we must
|
||||
provide a more convenient syntax for constructing immutable types in the same release. To do
|
||||
otherwise would be to effectively make a trap for users.
|
||||
|
||||
## Nominal vs Positional
|
||||
|
||||
We also continued to explore the space of nominal vs. positional records. An insight from the
|
||||
previous discussion is that nominal vs. positional records are really about improving syntax
|
||||
for type construction. Positional records are about taking the existing type construction,
|
||||
constructors, and giving them a shorter syntax. Nominal records are about identifying some
|
||||
weaknesses of the existing construction system and providing a new feature to support them. In
|
||||
both cases, though, the proposed features shorten construction by avoiding repeating the member
|
||||
declarations and the assignments.
|
||||
|
||||
We also had the following notes, in no particular order:
|
||||
|
||||
* For positional constructors, it's important to consider primary constructors. We have one
|
||||
proposal for primary constructors, but have not had a discussion on whether we want them, and if
|
||||
the syntax is worth taking away from the record syntax.
|
||||
|
||||
* If the `initonly` keyword is required, even for common cases, this is about
|
||||
as expensive syntax-wise as a setter. It may be a few more characters, but it
|
||||
keeps things on one line, as opposed to the multi-line constructors that are
|
||||
required right now.
|
||||
|
||||
* There's an opposition between evolving existing data types and picking and choosing features when
|
||||
you need them, but also providing a simple syntax for the most common case. Certainly positional
|
||||
records solve a common case. The question is how common that case is.
|
||||
|
||||
* Positional records use existing initialization strategy (construction) which is fairly
|
||||
well-understood. The initonly feature, by contrast, will force us to reexamine some assumptions.
|
||||
For instance, constructors take all inputs at once, meaning that you can enforce requirements
|
||||
between them at construction time. `initonly` overrides properties after construction, and
|
||||
one-by-one, so it's not possible, or not obvious, how to provide this functionality.
|
||||
|
||||
* There are mixed feelings on the requirements of what features will be available individually, and
|
||||
which are separable. Some people feel that addressing the most common case is sufficient for
|
||||
providing the value of the feature, namely that there can be a single "record" which provides
|
||||
immutable construction and value equality, and that is the only way to access these features.
|
||||
Others think that the features need to be adoptable independently: existing types need to be able
|
||||
to adopt generated equality without adopting immutable construction, while immutable construction
|
||||
needs to be available without structural equality.
|
||||
|
||||
One conclusion is that no proposal will solve all record-related problems. This is fine. We also
|
||||
have general agreement that there should be a convenient shorthand for the combination of most or
|
||||
all of the features (simple immutable construction, structural equality, etc.). Notably we don't
|
||||
all agree on whether or not structural equality will be the most common type of equality for
|
||||
records, but the shortest record form may include structural equality for other language design
|
||||
reasons.
|
|
@ -27,10 +27,6 @@
|
|||
- https://github.com/dotnet/csharplang/projects/4#column-4649189 Triage recently championed features
|
||||
- https://github.com/dotnet/csharplang/issues/2608 module initializers (Neal)
|
||||
|
||||
## Dec 11, 2019
|
||||
|
||||
- Feedback from Dec 4 design review (Andy, Mads)
|
||||
|
||||
## Dec 9, 2019
|
||||
|
||||
- https://github.com/dotnet/csharplang/issues/2850 Proposed changes for pattern-matching (continued) (Neal)
|
||||
|
@ -51,6 +47,12 @@
|
|||
|
||||
Overview of meetings and agendas for 2019
|
||||
|
||||
## Dec 11, 2019
|
||||
|
||||
[C# Language Design Notes for Dec 11, 2019](LDM-2019-12-11.md)
|
||||
|
||||
1. Design review feedback
|
||||
|
||||
## Nov 25, 2019
|
||||
|
||||
[C# Language Design Notes for Nov 25, 2019](LDM-2019-11-25.md)
|
||||
|
|
Loading…
Reference in a new issue