csharplang/meetings/2020/LDM-2020-05-27.md
NetMage 8cb28ad560
Update LDM-2020-05-27.md (#3875)
Replaced incorrect structs with classes in: "less far behind" than structs in record features
2020-09-10 18:56:10 -07:00

4.8 KiB

C# Language Design Meeting for May 27, 2020

Agenda

  1. Records -- syntax

Discussion

Syntax Questions

We got significant feedback that record is a better name than data for indicating a record. There are two syntaxes we've been considering here:

1. `record class Person { ... }`
2. `record Person { ... }`

The main difference here is that (2) has less obvious space for a struct token, which raises the question of whether "record structs" are a feature we want to enable.

There are a couple arguments for why records would be useful for structs. The first is that "value behavior" is a general feature that could be useful for both structs and classes. Value equality exists for structs today, but it is potentially slow in the runtime implementation.

The second is that the syntax provided for classes is also useful for structs. The positional syntax specifically seems attractive because it has a lot of similarity to tuples and allows a form of "named tuple."

record struct Point(int X, int Y);

On the other hand, we could improve the performance of equality, completely separate from records. For instance, the compiler could add equality methods if they are not present. We also do not necessarily need to address structs first. Since structs already have many features of records they are, in a sense, "less far behind" than classes in record features. It makes sense to concentrate first on classes and consider augmentations for structs in a future update.

So to return to the original question, we have to decide if we want to move forward with option (2), which is a new form of declaration. Notably, this is a breaking change for certain scenarios e.g.,

record M(int X, int Y) { ... }

class C
{
    record M(int X, int Y) { ... }
    partial record M(int X, int Y);
}

All of these are currently method declaration syntax. In C# 9 this would be a record declaration with a positional constructor and a body. Normally we would never consider this kind of change, but since we started shipping with .NET Core we do not automatically upgrade language version unless the target framework is the newest one (i.e., NET 5).

Conclusion

Do not support structs for now. They already support many features of records and we can add more, time permitting.

The accepted proposal is that the syntax, <modifiers> <attributes> 'record' followed by identifier and either '(', '{', or '<' would be contextually parsed as a record declaration only if the language version is C# 9.

Short-property syntax

We previously agreed that, to unify the syntax forms in the positional and nominal declaration, we would allow fields in nominal records with no modifiers to instead be interpreted as public auto-properties. After looking at feedback and exploring some of the related issues, we've decided that's not the best approach.

There are a few proposals on the table:

1. Leave positional records the same, do not provide special syntax for nominal records.
public record Point(int X, int Y);
public record Point
{
    public int X { get; init; }
    public int Y { get; init; }
}
2. Unify the declaration forms in favor of nominal records, allowing property declarations in the
record parameter list
public record Point(
    public int X { get; init; },
    public int Y { get; init; }
)
public record Point
{
    public int X { get; init; },
    public int Y { get; init; }
}
3. Keep positional records the same, provide a new modifier (e.g., `data` or `init`) for members
which means "public init-only property"
public record Point(int X, int Y);
public record Point
{
    data int X;
    data int Y;
}
4. Provide the new modifier from (3), and require it in both types of records
public record Point(data int X, data int Y);
public record Point
{
    data int X;
    data int Y;
}

After discussion, we prefer (3). Positional records already seem to have enough syntactic distinction and the data keywords seem superfluous in this position. It also makes the shortest syntax form match up with the most common use case.

However, we do think some further keyword is necessary for nominal records. Looking too much like existing fields seems like it would be too confusing, especially if we want to also allow fields in records.

Instead, we're considering leaving positional records to generate public auto-properties by default, partly because they are already a significantly different syntax that cannot be confused with existing language constructs, and providing a new mechanism for positional records.

Conclusion

Keep positional records the same, provide a data modifier for fields which means "public init-only property"

public record Point(int X, int Y);
public record Point
{
    data int X;
    data int Y;
}