csharplang/meetings/2016/LDM-2016-11-15.md

89 lines
3.8 KiB
Markdown
Raw Normal View History

C# Language Design Notes, Nov 15, 2016
======================================
> *Quote of the day*: "Bad people don't get good features!"
Agenda
------
- Tuple name warnings
- "Discards"
Tuple name warnings
===================
There are two kinds of warnings on the table regarding names in tuples:
1. A warning on tuple *literals* only, if the literal uses an element name that isn't in the target type
2. A warning on all expressions of tuple type if an element name in the expression is used for a *different* element in the target type
The first warning helps get the names right when writing a tuple literal, and while maintaining it. It is already in the product:
``` c#
(int x, int y) t = (a: 1, b: 2); // Oops, forgot to update tuple literal? Names a and b would be lost
```
The second warning would help guard against bugs due to reordering of tuple elements:
``` c#
(string firstName, string lastName) GetName();
(string lastName, string firstName) name = GetName(); // Oops, forgot to swap the element names in name?
```
This warning is currently *not* in the product. We would like it to be, but don't have the runway to implement it.
In the future it could be added as an analyzer, or as a compiler warning protected by warning waves (#1580).
"Discards"
==========
We got great feedback on "wildcards" at the MVP Summit, including changing its name to the term "discards", which is spreading in industry.
We are also encouraged to use `_` as the discard character instead of `*`. It works better visually, and is what many other languages already use.
There is a small matter of `_` already being a valid identifier. In order for it to coexist as a discard with existing valid uses we need to use a few tricks. Here are the essential rules:
1. A standalone `_` when no `_` is defined in scope is a discard
2. A "designator" `var _` or `T _` in deconstruction, pattern matching and out vars is a discard
Discards are like unassigned variables, and do not have a value. They can only occur in contexts where they are assigned to.
Examples:
``` c#
M(out _, out var _, out int _); // three out variable discards
(_, var _, int _) = GetCoordinates(); // deconstruction into discards
if (x is var _ && y is int _) { ... } // discards in patterns
```
One use case is to silence warnings when dropping Tasks from async methods:
``` c#
_ = FooAsync();
```
We also want to allow discards as lambda parameters `(_, _) => 0`, but don't expect that to make it in C# 7.0. Today `_` is allowed as an ordinary identifier; the rule would be that it would become a discard if there's more than one.
We can also consider allowing top-level discards in foreach loops:
``` c#
foreach (_ in e) { ... } // I don't care about the values
```
But that does not seem too important, especially since you can use `var _` to the same effect.
In general, whenever use of standalone `_` as a discard is precluded, either by syntactic restrictions or by `_` being declared in the scope, `var _` is often a fine substitute with the same meaning. However, if the declared `_` is a parameter or local, there are situations where you are out of luck:
``` c#
public override void M(int _) // declaring _ to signal that this override doesn't care about it
{
_ = TryFoo(); // Error: cannot assign bool result to int variable _
var _ = TryFoo(); // Error: cannot declare local _ when one is already in scope
}
```
These situations are always local to a member, so it is relatively easy to rewrite them, e.g. to rename the incoming parameter. We could consider accommodating them in the future: the error for a local redeclaration of `_` when one is already in scope could be changed to allowing it, but consider the second declaration a discard. This may be too subtle, and not useful enough, but it is worth considering post C# 7.0.