
# C# LDM Notes for July 22, 2019

## Agenda

1. Discuss the [new records proposal](https://github.com/dotnet/csharplang/blob/856c335cc584eda2178f0604cc845ef200d89f97/proposals/recordsv2.md)

## Discussion

We started by going over the proposal.

A few comments and clarifications:

#### Optional and required initialization

The proposal doesn't mention what we consider a problem in existing
C# object initializer code: there's no way to specific that members are
required or optional. Most notably, a class has no way to force an object
initializer to set a member, and no diagnostics will be reported if it is
skipped.

The new proposal would produce an error if an `initonly` member is not
set either in the constructor, in an initializer, or in the object initializer.
A suggested implementation would be to emit an attribute that indicates whether
a member initialization is required or optional, but we do not mandate a specific
implementation strategy.

### Verification

The philosophy behind the proposed "initonly" verification rules are that we're broadening the
CLR's rules slightly. In the CLR, `initonly` (which is termed `readonly` in C#) makes the claim
that the end of the constructor is the publish boundary for a type, so `initonly` fields are
settable until the constructor finishes, at which point the object is published. We're widening
this slightly, by creating rules that ensure that even after the constructor has finished, values
can be set before the item is actually published either via a store or a method call.
As long as these rules could be efficiently written for a theoretical verifier,
we don't strictly need to have an implementation concurrent with the feature.

### With-constructor

It also looks like some of the compilicated codegen around the body of the `With` constructor may be unnecessary. The default implementation may be completely
satisfied by `object.MemberwiseClone()`. However, a With-constructor would likely
still be necessary, as `object.MemberwiseClone()` is protected, and we would like
to allow for customizability.

### Mutability and code generation

For classes, it would also be good to warn if mutable members are included in a `data` class,
because structural equality is dangerous in the presence of mutability (e.g., if a class is
changed after being added to a dictionary, it can "disappear" from the dictionary).

### Primary constructors and attributes

The proposed use for primary constructors is short and simple data types. It
was noted that many serialization frameworks require a large set of attributes
to specify serialization details. 
For the primary constructor feature, it's an open question of how attributes will
work. There is a proposal for putting
attributes on the parameters with an additional target, e.g.

```C#
data class C([property: MyAttribute]int X);
```

However, this harms the concision of the feature and may mean that primary constructors are not
as useful for serialized types.

### Criticisms of new proposal vs original

One major difference between the new proposal and the old proposal is that it implies the
existence of a record without a primary constructor. The original motivation for this was to
avoid encoding the constructor's positional API (constructors are dependent on argument order)
into record construction and use. Thus, to differentiate the two proposals we can refer to them
as "nominal" records (records V2) and "positional" records (records V1).

* Primary constructors as a whole: it's not clear if the pieces are actually that useful outside of
the records proposal.

* Primary constructors + initialization. If people like object initializers, one
possible solution is what is proposed here. Another possible solution is to make an object
initializer for positional records as a simple syntactic transformation to a record constructor.

* Source/Binary compatibility. Positional records do not solve the problems
of adding or re-ordering members, but it is solvable by manually writing
back-compat constructors when adding members.

* Use cases: it seems like the common case for a few fields (like the
UserInfo example) would be more the positional record case. Instead, the
common case for nominal records would be big records, like Roslyn's
CompilationOptions.

* Validation: there's no way to validate the consistency of the whole object
in this proposal. For instance, many records have requirements that if one field
is set, another is set as well, or that they have compatible values. Unfortunately,
this is also true of "positional" records, since the With-ers would set values
after the construction of the copy completes, meaning that no validation run in
the constructor would validate the "With"-ed members. One way to add this functionality would be
to enforce a call to a well-known validation method after construction is done.