
# C# Language Design Meeting for Jan. 15th, 2020

## Agenda

1. Working with data
2. Record feature breakdown

## Discussion

### Working with data

As we discuss records, we want to go over a design document we produced a number of years ago
called "working with data." This document lays out how, when we design features, we inherently
express a "path of least resistance," which consists of the features that seem easiest or
shortest to use to accomplish a given problem.

Link: https://github.com/dotnet/csharplang/issues/3107

The document argues that, as we find particular patterns to be more effective at building
software, we should make the forms we find to be more effective simpler or shorter to express in
the language. We should not "change" our opinions, meaning make old syntax illegal, but we should
"level the playing field" by making other forms simpler.

The conclusion of the design document is that we should favor

    1. Immutable members in records by default
    1. Any features from records that we separate should not make the simple syntax longer

### Record feature breakdown

We've also been working on breaking down the individual features
of records and determining how independent they can or should be.

Notes: https://github.com/dotnet/csharplang/issues/3137

There seem to be the following somewhat separable parts of records

1. Value-based equality
2. Construction boilerplate
3. Object initializers
4. Nondestructive mutation
5. Data-friendly defaults

#### Value equality

It's been proposed that a `key` modifier could be applied to signal that value-based equality is
being generated based on the members which have it. This works in many cases,
but if the absence of the `key` modifier means inherited equality, we're not sure
that's the semantics we want. It would also not allow value-based equality to be
"specified" in the base class in some sense, enforcing value equality for the deriving
type. Whether this is valuable or blocking is an open question.

#### Construction boilerplate

Creating a constructor to assign members of a container is one of the largest sources
of repetitive boilerplate, e.g.

```C#
public class Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}
```

You can imagine various points on this spectrum to simplify the boilerplate,

```C#
public class Point
{
    public key int X { get; }
    public key int Y { get; }
    public Point(X, Y); // name matching and type absence implies initialization
}
```

Which removes the duplication of naming the same elements multiple times or,

```C#
public class Point(X, Y)
{
    public key int X { get; }
    public key int Y { get; }
}
```

Which removes the constructor name duplication and we could go further to remove
property name duplication,

```C#
public class Point(
    public key int X { get; }
    public key int Y { get; }
);
```

Going all the way to the original position deconstruction

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

Where we pick a point in this space seems to correspond to the perceived benefits
of the orthogonality of the feature. If the construction shorthand is useful for
many scenarios outside of the record scenarios, it's practical to expand it.

### Object initializers

One benefit to object initializers is that they don't refer to a constructor directly,
only to the properties. This sidesteps a weakness in C#, where constructor initialization in inheritance requires repetition. Without constructors the simple
relation

```C#
public abstract class Person
{
    public string Name { get; }
}
public class Student : Person
{
    public string ID { get;}
}
```

has no repetition. Each class states only the properties that are essential, and
for derived classes all the base properties are inherited without repetition.
Once you add constructors this breaks down

```C#
public abstract class Person
{
    public string Name { get; }
    public Person(string name)
    {
        Name = name;
    }
}
public class Student : Person
{
    public string ID { get; }
    public Student(string id, string name)
      : base(name)
    {
        Id = id;
    }
}
```

Now the derived classes have to repeat everything from the base, causing brittleness
along the boundary. If we were to imagine some improvement to object initializers,
then defining a constructor would not be required.

On the other hand, this also removes one of the main benefits for having a constructor,
namely that you can validate the whole state of the object before producing it.