diff --git a/proposals/named-tuples.md b/proposals/named-tuples.md new file mode 100644 index 0000000000..de48056805 --- /dev/null +++ b/proposals/named-tuples.md @@ -0,0 +1,112 @@ + +# Tuples: Small to Big + +When tuples were added in C# 7 they brought a few new things to the language + + 1. A short, simple syntax for grouping pieces of data + 2. A way to group simple data beyond method boundaries with automatic structural equality + 3. First-class support for pattern matching and destructuring with positional semantics + +In retrospect, it looks like tuples have done a very good job of addressing these pain points +for small sets of data across short pieces of a program. This is great, but it presents a +problem when you decide that your set of data has either grown too large to represent in a tuple, +or you decide that you want that tuple to be a core type in large parts of your program. Since +a tuple is not really its own type and instead is simply a composite of its members, this forces +you to repeat the type declaration at every reference point, which quickly becomes laborious if +you either have many tuple members or you use the tuple in many places. In addition, the more +the tuple type becomes central to the design of a piece of your program, the more the structural +typing becomes a problem and the desire for traditional C# nominal typing becomes prominent. + +This is generally the point where most users will want to evolve their tuple type into a proper +named type. Unfortunately, this is extremely laborious. Now the following must be defined manually: + + 1. `ItemX` properties for each positional member of the type + 2. Optionally, custom named properties for each positional member of the type + 3. A constructor to assign each of the input parameters + 4. A deconstructor to support pattern matching + 4. Memberwise equality, including `GetHashCode` and `IEquatable` + +While the associated `data` named types makes some of this easier, the positionality and tuple +characteristics are not addressed and are orthogonal to the former proposal. + +# Proposal + +To solve these problems, I propose what I'm calling "named tuples." + +A named tuple has the following syntax for classes, with an analogous definition for structs: + +```antlr +class_declaration + : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list? + parameter_list? class_base? type_parameter_constraints_clause* (class_body | ';')? + ; + +parameter_list + : '(' parameters ')' + ; + +parameters + : attributes? parameter + | parameters ',' attributes? parameter + ; + +parameter + : 'readonly'? identifier identifier? + ; +``` + +For example, a Cartesian "point" class could like the following as a named tuple: + +```C# +class Point(int, int); + +void M() +{ + var p = new Point(0, 0); +} +``` + +Like tuples, names of the elements are optional. If no names are provided, a consumer +can refer to automatically generated `ItemX` properties, as well as use the automatically +generated Deconstruct method, e.g. + +```C# +void M() +{ + var (x, y) = new Point(0, 0); +} +``` + +In the simplest form, the body of the class is also optional. + +## Generated members + +From the definition of `data` named types, you can see that tuples already conform to many +of the same semantics. The same is true of named tuples. Equality is generated like +for `data` named types, but the `data` keyword is not needed. One difference between equality +in named tuples and equality in anonymous tuples is that anonymous tuples are structurally +equal, while named tuples follow the C# standard of nominal equality for named types. This +means that one named tuple instance can only be equal to another named tuple instance if they +are the same type, not if they just have equal members. + +In addition to the equality members, named tuples also generate + + 1. A constructor corresponding to the parameter list in the type definition. + 2. `ItemX` properties for each member + 3. If a parameter is named in the parameter list, a public property with the same name + that gets/sets that member. + 4. A Deconstruct method corresponding to the parameter list. + +If the `readonly` modifier precedes any of the member types in the parameter list, all +autogenerated properties for that parameter are get-only properties. + +## Customization + +If any of the members which would be automatically generated are manually specified in +the body of the class, those members skip auto-generation, including the constructor. It +is not illegal to define a constructor with the same signature. However, the parameter +names of the constructor are not considered when assigning names for automatically generating +member properties. + +Note that, unlike in the `data` named type proposal, there is no special behavior for +object initializers, so an unspeakable initialization method will not be generated. \ No newline at end of file