Add LDM notes for Feb. 12, 2020
This commit is contained in:
parent
b090ab8413
commit
99094464ff
116
meetings/2020/LDM-2020-02-12.md
Normal file
116
meetings/2020/LDM-2020-02-12.md
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
|
||||||
|
# C# Language Design for Feb 12, 2020
|
||||||
|
|
||||||
|
## Agenda
|
||||||
|
|
||||||
|
Records
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
### Value equality
|
||||||
|
|
||||||
|
Proposal: use the `key` keyword previously mentioned, but also
|
||||||
|
require it on the type declaration as well, e.g.
|
||||||
|
|
||||||
|
```C#
|
||||||
|
key class HasValueEquality
|
||||||
|
{
|
||||||
|
public key int X { get; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a number of things we could pivot on
|
||||||
|
|
||||||
|
```C#
|
||||||
|
key class HasValueEquality1 { public key int X { get; } }
|
||||||
|
class HasValueEquality2 { public key int X { get; } }
|
||||||
|
key class HasValueEquality3 { public key X { get; } }
|
||||||
|
class HasValueEquality4 : IEquatable<HasValueEquality4> { public int X { get; } }
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
```C#
|
||||||
|
record Point1(int X); // Implies value equality over X
|
||||||
|
record Point2a(int X); // Implies inherited equality
|
||||||
|
key record Point2b1(int X); // Implies value equality over X
|
||||||
|
key record Point2b2a(int X); // Implies "empty" value equality
|
||||||
|
key record Point2b2b(key int X); // Implies value equality over X
|
||||||
|
|
||||||
|
|
||||||
|
key class Point3a(int X); // implies record + value equality over X
|
||||||
|
data class Point3b(int X); // implies record with inherited equality
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Equality default
|
||||||
|
|
||||||
|
We originally considered adding value equality on records both because it's difficult to
|
||||||
|
implement yourself and it fits the semantics we built for records in general. We want to validate
|
||||||
|
that these things are still true, and new considerations, namely whether it is the appropriate
|
||||||
|
default for records and whether it should be available to other types, like regular classes.
|
||||||
|
|
||||||
|
We left off in the previous discussion asking whether value equality is not just
|
||||||
|
an inconvenient default, but actively harmful for key scenarios for records. Some examples
|
||||||
|
we came up with are either classes with large numbers of members, where value equality may
|
||||||
|
be unnecessary and slow, and circular graphs, where using value equality could cause
|
||||||
|
infinite recursion.
|
||||||
|
|
||||||
|
These do seem bad, but it's not obvious that these scenarios either fit perfectly with the
|
||||||
|
canonical record, or if the consequences are necessarily worse than default reference equality.
|
||||||
|
Certainly producing infinite recursion in object graphs is bad, but silently incorrect behavior
|
||||||
|
due to inaccurate reference equality is also harmful, in the same sense. It's also easier
|
||||||
|
to switch from value equality to reference equality than it is to switch from reference equality
|
||||||
|
to value equality, due to the complex requirements in a value equality contract.
|
||||||
|
|
||||||
|
**Conclusion**
|
||||||
|
|
||||||
|
Value equality seems a reasonable default, as long as they are immutable by default, and that
|
||||||
|
there is a reasonable way to opt-in to a different equality.
|
||||||
|
|
||||||
|
#### Separable value equality
|
||||||
|
|
||||||
|
Given that we like value equality as a default, we have to decide if we want a separable equality
|
||||||
|
feature as well. This is important for the scenario:
|
||||||
|
|
||||||
|
```C#
|
||||||
|
record Point1(int X)
|
||||||
|
{
|
||||||
|
public int X { get; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
if there's a separate `key` feature, we need to decide if the substituted property should
|
||||||
|
require, allow, or disallow the `key` modifier, e.g.
|
||||||
|
|
||||||
|
```C#
|
||||||
|
record Point1(int X)
|
||||||
|
{
|
||||||
|
public key int X { get; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We also need to decide what such a "separable" equality feature would look like, and if it has a
|
||||||
|
difference between records and other classes. We could add a `key` feature for non-records, and
|
||||||
|
disallow `key` entirely in records. The members of a record equality would then not be
|
||||||
|
customizable.
|
||||||
|
|
||||||
|
The individual `key` modifiers on non-records seem deceptively complicated.
|
||||||
|
|
||||||
|
A common case is "opt-in everything". `key` modifiers wouldn't improve much on this, as they
|
||||||
|
would be necessary on every element. On the other hand, there are often computed properties that
|
||||||
|
may be seen as part of "everything", but not part of the equality inputs. The plus of record
|
||||||
|
primary constructors is that they identify the "core" inputs to the type.
|
||||||
|
|
||||||
|
Individual `key` modifiers also do not help with the large custom classes that are written today
|
||||||
|
where it's easy to forget to add new members to equality. With a `key` modifier you can still
|
||||||
|
forget to add the modifier to a new member.
|
||||||
|
|
||||||
|
These decisions play into records as a whole because they affect the uniformity of record and
|
||||||
|
non-record behavior. If records are defined by their "parameters", namely in this syntax the
|
||||||
|
primary constructor parameters and identically named properties, then no other members should
|
||||||
|
be a part of the equality. However, that would imply members in the body are not automatically
|
||||||
|
included. For regular classes, it seems backwards. Members are not generally included, they have
|
||||||
|
to be added specifically.
|
||||||
|
|
||||||
|
On the other hand, if we prioritize uniformity, general members in record bodies would be included
|
||||||
|
in equality, which would harm a view of records as consisting primarily of the "record inputs."
|
|
@ -26,14 +26,6 @@
|
||||||
- https://github.com/dotnet/csharplang/issues/3213 Natural value equality (Mads)
|
- https://github.com/dotnet/csharplang/issues/3213 Natural value equality (Mads)
|
||||||
- https://github.com/dotnet/csharplang/issues/3137 Records (Mads)
|
- https://github.com/dotnet/csharplang/issues/3137 Records (Mads)
|
||||||
|
|
||||||
## Feb 12
|
|
||||||
|
|
||||||
- https://github.com/dotnet/csharplang/issues/3137 Records (Mads)
|
|
||||||
|
|
||||||
## Feb 10
|
|
||||||
|
|
||||||
- https://github.com/dotnet/csharplang/issues/3137 Records (Mads)
|
|
||||||
|
|
||||||
## Jan 29, 2020
|
## Jan 29, 2020
|
||||||
|
|
||||||
- Records: drilling in to individual features (Mads)
|
- Records: drilling in to individual features (Mads)
|
||||||
|
@ -46,6 +38,18 @@
|
||||||
|
|
||||||
Overview of meetings and agendas for 2020
|
Overview of meetings and agendas for 2020
|
||||||
|
|
||||||
|
## Feb 12
|
||||||
|
|
||||||
|
[C# Language Design Notes for Feb. 12, 2020](LDM-2020-02-12.md)
|
||||||
|
|
||||||
|
Records
|
||||||
|
|
||||||
|
## Feb 10
|
||||||
|
|
||||||
|
[C# Language Design Notes for Feb. 10, 2020](LDM-2020-02-10.md)
|
||||||
|
|
||||||
|
Records
|
||||||
|
|
||||||
## Feb 5
|
## Feb 5
|
||||||
|
|
||||||
[C# Language Design Notes for Feb. 5, 2020](LDM-2020-02-05.md)
|
[C# Language Design Notes for Feb. 5, 2020](LDM-2020-02-05.md)
|
||||||
|
|
Loading…
Reference in a new issue