# C# Language Design for May 4, 2020

## Agenda

Design review feedback

## Discussion

We had a design review on 2020-04-29 to bring our latest designs to the full review team and get
feedback. Today we went over the feedback and how it would affect our design.

### Final initializers

    - Design review said it was very complicated, when do I use an initializer vs a constructor?

A possible fix would be to try to run initializers *before* constructors, instead of after. The
main problem is that this is not where object initializers (using setters) run today. It would be
very distasteful to have `init-only` setters run at a different time from regular setters, and
worse to subtly run the setters at a different time just because of the presence of a different
`init-only` field.

This is a difficult piece of feedback to reconcile, because it doesn't present a clear direction.
However, we're not sure we need to finish the design for final initializers now. We still think
the scenarios are useful, but there are many scenarios which don't rely on those semantics. One
of the most important scenarios that we were worried about was how to copy a type that had
private fields that should not be copied. One proposal was to write a final initializer which
either resets certain fields, or `throw`s if the state is invalid. Our proposed alternative for
this situation is to write your own copy constructor, which sets up the appropriate state for the
copy.

However, final initializers do address a significant shortfall in existing scenarios, namely that
there's no way to validate a whole object in a property setter (or initter). In that sense we do
have many existing issues, separate from our records designs, which would be addressable with the
feature. There is also no way to validate an object after a `with` expression since necessarily.

### Factory methods

The review team agreed about the necessity of "factory" semantics in the `with` expression, namely
that the with expression essentially requires a `virtual` Clone method to work correctly through
inheritance, but was not convinced that the feature was generally useful.

We're also not convinced that it's generally useful, but limiting `with` to only be usable on a
record is a significant change from where we were before, where records are currently fully
representable as regular classes.

We need to consider if we are willing to live with this limitation, or need a way of specifying
the appropriate `Clone` method in source.

### Structs as records

Can every struct be a record automatically? We don't need a `Clone` method, because structs
already copy themselves and they already implement value equality (albeit sometimes
inefficiently). If we take this stance, would we want to explicitly design records as "struct
behavior for classes?" If that's true, we would seek to use the behavior of structs as a template
for records.

### Positional records

The feedback was negative about making a primary constructor parameters different from positional
record parameters. The proposal during the design meeting was that primary constructors would see
parameters as "captured" in the scope of the class, while records would generate public
properties for each parameter. This is a big semantic divergence, as expressions like
`this.parameter` would be legal in the body of a positional record, but illegal in the body of a
class with a primary constructor. One way of shrinking the semantic gap would be to always
generate members based on primary constructor parameters, but in regular classes those members
would be private fields, while in records they would be public init-only properties. Even this
semantic difference was perceived as too inconsistent.

We have two proposals to unify the behavior inside and outside of records. On one end, we could
try to view primary constructors as a syntactic space to contain more elements. By default,
primary constructors would be simple parameters, which could be closed over in the class body. By
allowing member syntax in the parameter list, the user would have more control over the
declaration. For instance,

```C#
public class Person(
    public string Name { get; init; }
);
```

would generate a public property named `Name` instead of simply a parameter and the property
would be implicitly assigned in the constructor.

On the other hand, we could _always_ make public properties, abandoning the idea of
primary-constructor-parameters-as-closures. In this formulation,

```C#
class C(int X, int Y);
```

would generate two properties, X and Y. If this is made into a record e.g., `data class C(int X,
int Y)`, then the same record members would be synthesized as in a nominal record.

We did not settle on a conclusion, but have a rough sense that having a primary constructor
always generate properties is preferred.
