
# C# Language Design Meeting for May 11, 2020

## Agenda

Records

## Discussion

Today we tried to resolve some of the biggest questions about records,
namely how to unify the semantics of nominal records and positional
records, and what are the key scenarios that we are trying to resolve
with records.

The main inconsistency is that the members `record class Person(string FirstName, string
LastName)` are very different from the members in `class Person(string firstName, string
lastName)`. One way of resolving this is to unify the meaning of the declaration in the direction
of primary constructors. In this variant, the parameters of a primary constructor always capture
items by default.

To produce public init-only properties like we were exploring, we would require an extra
keyword, `data`, that could be generalizable. So a record which has two public init-only
members would be written

```C#
record Person(data string FirstName, data string LastName);
```

This would allow a generalizable `data` keyword that could be applied even in regular
classes, e.g.

```C#
class Person
{
    data string FirstName;
    data string LastName;
    public Person(string first, string last)
    {

    }
}
```

The worry here is that we're harming an essential motivation for records, namely a short syntax
for immutable data types. In the above syntax, `record` alone does not mean immutable data type,
but instead only value equality and non-destructive mutation. A problem with this is that value
equality is dangerous for mutable classes, since the hash code can change after being added to a
dictionary. This was why we were previously cautious about adding a general feature for value
equality. One option to discourage misuse would be to provide a warning for any non-immutable
member in a record class.

The other problem is, frankly, it's not that short. Aside from some duplication of intent by
requiring both `record` and `data` modifiers, it also requires applying the `data` modifier to
each member, so the overhead grows larger as the type does.

Alternatively, we could go in the complete opposite direction: limit customizability by making
records all about public immutable data.

For instance, nominal records could also have syntax abbreviation

```C#
record Person { string FirstName; string LastName; }
```

and we could avoid confusion by prohibiting other members entirely.

This would look at lot more like positional records, e.g.

```C#
record Person(string FirstName, string LastName);
```

and we could introduce further restrictions on those by also disallowing other members
in the body, or even disallowing primary constructors entirely.

Disallowing all members inside of records is draconian, but not entirely without precedence.
Enums work the same way in C# and members are added via extensions methods. That's not a ringing
endorsement since we've considered proposals for allowing members in enums before, but it also
doesn't put it outside the realm of possibility for C#, especially in earlier forms.

The main drawback of the simplest form is the risk that we might have trouble evolving the
feature to fit all circumstances. If we wanted to allow a user to define private fields, the
syntax with no accessibility modifier now means "public init-only property" so we might not
be able to add support for private fields at all, or we might have to use a syntactic distinction
that requires a `private` accessibility, which is a subtle change.

### Conclusion

We largely prefer the short syntax for records. A nominal record would look like

```C#
record class Person { string FirstName; string LastName; }
```

This would create a class with public `init-only` properties named `FirstName`
and `LastName`, along with equality and non-destructive mutation methods.

Similarly,

```C#
record class Person(string FirstName, string LastName);
```

would create a class with all of the above, but also a constructor and Deconstruct.

We have yet to confirm whether `record` disallows private fields entirely, or if it
just changes the default accessibility.